diff options
Diffstat (limited to 'drivers/net/wireless/ath')
226 files changed, 37486 insertions, 5895 deletions
diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 815f8f599f5d..343c9de2749c 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -733,7 +733,7 @@ static void ar5523_data_tx_pkt_put(struct ar5523 *ar) { atomic_dec(&ar->tx_nr_total); if (!atomic_dec_return(&ar->tx_nr_pending)) { - del_timer(&ar->tx_wd_timer); + timer_delete(&ar->tx_wd_timer); wake_up(&ar->tx_flush_waitq); } @@ -902,7 +902,7 @@ static void ar5523_tx_work(struct work_struct *work) static void ar5523_tx_wd_timer(struct timer_list *t) { - struct ar5523 *ar = from_timer(ar, t, tx_wd_timer); + struct ar5523 *ar = timer_container_of(ar, t, tx_wd_timer); ar5523_dbg(ar, "TX watchdog timer triggered\n"); ieee80211_queue_work(ar->hw, &ar->tx_wd_work); @@ -1061,7 +1061,7 @@ err: return error; } -static void ar5523_stop(struct ieee80211_hw *hw) +static void ar5523_stop(struct ieee80211_hw *hw, bool suspend) { struct ar5523 *ar = hw->priv; @@ -1076,7 +1076,7 @@ static void ar5523_stop(struct ieee80211_hw *hw) ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0); - del_timer_sync(&ar->tx_wd_timer); + timer_delete_sync(&ar->tx_wd_timer); cancel_work_sync(&ar->tx_wd_work); cancel_work_sync(&ar->rx_refill_work); ar5523_cancel_rx_bufs(ar); @@ -1594,6 +1594,20 @@ static int ar5523_probe(struct usb_interface *intf, struct ar5523 *ar; int error = -ENOMEM; + static const u8 bulk_ep_addr[] = { + AR5523_CMD_TX_PIPE | USB_DIR_OUT, + AR5523_DATA_TX_PIPE | USB_DIR_OUT, + AR5523_CMD_RX_PIPE | USB_DIR_IN, + AR5523_DATA_RX_PIPE | USB_DIR_IN, + 0}; + + if (!usb_check_bulk_endpoints(intf, bulk_ep_addr)) { + dev_err(&dev->dev, + "Could not find all expected endpoints\n"); + error = -ENODEV; + goto out; + } + /* * Load firmware if the device requires it. This will return * -ENXIO on success and we'll get called back afer the usb diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index f02a308a9ffc..34654f710d8a 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -171,8 +171,10 @@ struct ath_common { unsigned int clockrate; spinlock_t cc_lock; - struct ath_cycle_counters cc_ani; - struct ath_cycle_counters cc_survey; + struct_group(cc, + struct ath_cycle_counters cc_ani; + struct ath_cycle_counters cc_survey; + ); struct ath_regulatory regulatory; struct ath_regulatory reg_world_copy; diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index e6ea884cafc1..876aed765833 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -45,6 +45,7 @@ config ATH10K_SNOC depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_SMEM + depends on QCOM_RPROC_COMMON || QCOM_RPROC_COMMON=n select QCOM_SCM select QCOM_QMI_HELPERS help @@ -67,6 +68,12 @@ config ATH10K_DEBUGFS If unsure, say Y to make it easier to debug problems. +config ATH10K_LEDS + bool + depends on ATH10K + depends on LEDS_CLASS=y || LEDS_CLASS=MAC80211 + default y + config ATH10K_SPECTRAL bool "Atheros ath10k spectral scan support" depends on ATH10K_DEBUGFS diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 142c777b287f..02bf9b629038 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -19,6 +19,7 @@ ath10k_core-$(CONFIG_ATH10K_SPECTRAL) += spectral.o ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o ath10k_core-$(CONFIG_THERMAL) += thermal.o +ath10k_core-$(CONFIG_ATH10K_LEDS) += leds.o ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o ath10k_core-$(CONFIG_PM) += wow.o ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index a378bc48b1d2..eb8b35b6224d 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -394,14 +394,14 @@ static irqreturn_t ath10k_ahb_interrupt_handler(int irq, void *arg) if (!ath10k_pci_irq_pending(ar)) return IRQ_NONE; - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); napi_schedule(&ar->napi); return IRQ_HANDLED; } -static int ath10k_ahb_request_irq_legacy(struct ath10k *ar) +static int ath10k_ahb_request_irq_intx(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); @@ -415,12 +415,12 @@ static int ath10k_ahb_request_irq_legacy(struct ath10k *ar) ar_ahb->irq, ret); return ret; } - ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_LEGACY; + ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_INTX; return 0; } -static void ath10k_ahb_release_irq_legacy(struct ath10k *ar) +static void ath10k_ahb_release_irq_intx(struct ath10k *ar) { struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); @@ -430,7 +430,7 @@ static void ath10k_ahb_release_irq_legacy(struct ath10k *ar) static void ath10k_ahb_irq_disable(struct ath10k *ar) { ath10k_ce_disable_interrupts(ar); - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); } static int ath10k_ahb_resource_init(struct ath10k *ar) @@ -497,7 +497,7 @@ static int ath10k_ahb_resource_init(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "irq: %d\n", ar_ahb->irq); - ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%pK mem_len: %lu gcc mem: 0x%pK tcsr_mem: 0x%pK\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%p mem_len: %lu gcc mem: 0x%p tcsr_mem: 0x%p\n", ar_ahb->mem, ar_ahb->mem_len, ar_ahb->gcc_mem, ar_ahb->tcsr_mem); return 0; @@ -621,7 +621,7 @@ static int ath10k_ahb_hif_start(struct ath10k *ar) ath10k_core_napi_enable(ar); ath10k_ce_enable_interrupts(ar); - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); ath10k_pci_rx_post(ar); @@ -775,7 +775,7 @@ static int ath10k_ahb_probe(struct platform_device *pdev) ath10k_pci_init_napi(ar); - ret = ath10k_ahb_request_irq_legacy(ar); + ret = ath10k_ahb_request_irq_intx(ar); if (ret) goto err_free_pipes; @@ -806,7 +806,7 @@ err_halt_device: ath10k_ahb_clock_disable(ar); err_free_irq: - ath10k_ahb_release_irq_legacy(ar); + ath10k_ahb_release_irq_intx(ar); err_free_pipes: ath10k_pci_release_resource(ar); @@ -828,7 +828,7 @@ static void ath10k_ahb_remove(struct platform_device *pdev) ath10k_core_unregister(ar); ath10k_ahb_irq_disable(ar); - ath10k_ahb_release_irq_legacy(ar); + ath10k_ahb_release_irq_intx(ar); ath10k_pci_release_resource(ar); ath10k_ahb_halt_chip(ar); ath10k_ahb_clock_disable(ar); @@ -837,12 +837,12 @@ static void ath10k_ahb_remove(struct platform_device *pdev) } static struct platform_driver ath10k_ahb_driver = { - .driver = { - .name = "ath10k_ahb", + .driver = { + .name = "ath10k_ahb", .of_match_table = ath10k_ahb_of_match, }, - .probe = ath10k_ahb_probe, - .remove_new = ath10k_ahb_remove, + .probe = ath10k_ahb_probe, + .remove = ath10k_ahb_remove, }; int ath10k_ahb_init(void) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 9a4f8e815412..48efdc71d54d 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -349,7 +349,7 @@ static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 l int ret; size_t buf_len; - ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%p length %d\n", buffer, length); if (ar->bmi.done_sent) { @@ -395,7 +395,7 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) u32 txlen; int ret; - ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", buffer, length); if (ar->bmi.done_sent) { @@ -461,7 +461,7 @@ int ath10k_bmi_fast_download(struct ath10k *ar, int ret; ath10k_dbg(ar, ATH10K_DBG_BMI, - "bmi fast download address 0x%x buffer 0x%pK length %d\n", + "bmi fast download address 0x%x buffer 0x%p length %d\n", address, buffer, length); ret = ath10k_bmi_lz_stream_start(ar, address); diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index afae4a8027f8..a89a7491a76c 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -80,7 +80,7 @@ static inline u32 shadow_sr_wr_ind_addr(struct ath10k *ar, static inline unsigned int ath10k_set_ring_byte(unsigned int offset, - struct ath10k_hw_ce_regs_addr_map *addr_map) + const struct ath10k_hw_ce_regs_addr_map *addr_map) { return ((offset << addr_map->lsb) & addr_map->mask); } @@ -203,7 +203,7 @@ static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ctrl_regs->addr); @@ -217,7 +217,7 @@ static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ctrl_regs->addr); @@ -231,7 +231,7 @@ static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ctrl_regs->addr); @@ -313,7 +313,7 @@ static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; + const struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr, @@ -325,7 +325,7 @@ static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; + const struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr, @@ -337,7 +337,7 @@ static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; + const struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr, @@ -349,7 +349,7 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; + const struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr, @@ -360,7 +360,7 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; + const struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); @@ -372,7 +372,7 @@ static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; + const struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); @@ -384,7 +384,7 @@ static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); @@ -396,7 +396,7 @@ static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, static inline void ath10k_ce_error_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; + const struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; u32 misc_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr); @@ -410,7 +410,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int mask) { - struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); } @@ -1230,7 +1230,7 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) { struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; - struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; u32 ctrl_addr = ce_state->ctrl_addr; /* @@ -1388,7 +1388,7 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot init ce src ring id %d entries %d base_addr %pK\n", + "boot init ce src ring id %d entries %d base_addr %p\n", ce_id, nentries, src_ring->base_addr_owner_space); return 0; @@ -1426,7 +1426,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot ce dest ring id %d entries %d base_addr %pK\n", + "boot ce dest ring id %d entries %d base_addr %p\n", ce_id, nentries, dest_ring->base_addr_owner_space); return 0; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 9ce6f49ab261..fe3a8f4a1cc1 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -27,6 +27,7 @@ #include "testmode.h" #include "wmi-ops.h" #include "coredump.h" +#include "leds.h" unsigned int ath10k_debug_mask; EXPORT_SYMBOL(ath10k_debug_mask); @@ -68,6 +69,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca988x hw2.0", .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 1, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, @@ -75,7 +77,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 2116, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, - .board = QCA988X_HW_2_0_BOARD_DATA_FILE, .board_size = QCA988X_BOARD_DATA_SZ, .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, @@ -109,6 +110,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca988x hw2.0 ubiquiti", .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 0, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, @@ -116,7 +118,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 2116, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, - .board = QCA988X_HW_2_0_BOARD_DATA_FILE, .board_size = QCA988X_BOARD_DATA_SZ, .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, @@ -151,6 +152,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9887 hw1.0", .patch_load_addr = QCA9887_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 1, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, @@ -158,7 +160,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 2116, .fw = { .dir = QCA9887_HW_1_0_FW_DIR, - .board = QCA9887_HW_1_0_BOARD_DATA_FILE, .board_size = QCA9887_BOARD_DATA_SZ, .board_ext_size = QCA9887_BOARD_EXT_DATA_SZ, }, @@ -193,13 +194,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca6174 hw3.2 sdio", .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR, .uart_pin = 19, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 0, .fw = { .dir = QCA6174_HW_3_0_FW_DIR, - .board = QCA6174_HW_3_0_BOARD_DATA_FILE, .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, @@ -230,13 +231,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca6164 hw2.1", .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR, .uart_pin = 6, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 8124, .fw = { .dir = QCA6174_HW_2_1_FW_DIR, - .board = QCA6174_HW_2_1_BOARD_DATA_FILE, .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, @@ -271,13 +272,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca6174 hw2.1", .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR, .uart_pin = 6, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 8124, .fw = { .dir = QCA6174_HW_2_1_FW_DIR, - .board = QCA6174_HW_2_1_BOARD_DATA_FILE, .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, @@ -312,13 +313,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca6174 hw3.0", .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR, .uart_pin = 6, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 8124, .fw = { .dir = QCA6174_HW_3_0_FW_DIR, - .board = QCA6174_HW_3_0_BOARD_DATA_FILE, .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, @@ -353,6 +354,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca6174 hw3.2", .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR, .uart_pin = 6, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, @@ -360,7 +362,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .fw = { /* uses same binaries as hw3.0 */ .dir = QCA6174_HW_3_0_FW_DIR, - .board = QCA6174_HW_3_0_BOARD_DATA_FILE, .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, @@ -398,6 +399,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca99x0 hw2.0", .patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 17, .otp_exe_param = 0x00000700, .continuous_frag_desc = true, .cck_rate_map_rev2 = true, @@ -409,7 +411,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 12064, .fw = { .dir = QCA99X0_HW_2_0_FW_DIR, - .board = QCA99X0_HW_2_0_BOARD_DATA_FILE, .board_size = QCA99X0_BOARD_DATA_SZ, .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, @@ -445,6 +446,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9984/qca9994 hw1.0", .patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 17, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, .otp_exe_param = 0x00000700, .continuous_frag_desc = true, @@ -457,8 +459,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 12064, .fw = { .dir = QCA9984_HW_1_0_FW_DIR, - .board = QCA9984_HW_1_0_BOARD_DATA_FILE, - .eboard = QCA9984_HW_1_0_EBOARD_DATA_FILE, .board_size = QCA99X0_BOARD_DATA_SZ, .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, .ext_board_size = QCA99X0_EXT_BOARD_DATA_SZ, @@ -499,6 +499,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9888 hw2.0", .patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 17, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, .otp_exe_param = 0x00000700, .continuous_frag_desc = true, @@ -510,7 +511,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 12064, .fw = { .dir = QCA9888_HW_2_0_FW_DIR, - .board = QCA9888_HW_2_0_BOARD_DATA_FILE, .board_size = QCA99X0_BOARD_DATA_SZ, .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, @@ -550,13 +550,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9377 hw1.0", .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 6, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 8124, .fw = { .dir = QCA9377_HW_1_0_FW_DIR, - .board = QCA9377_HW_1_0_BOARD_DATA_FILE, .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, @@ -591,13 +591,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9377 hw1.1", .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 6, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 8124, .fw = { .dir = QCA9377_HW_1_0_FW_DIR, - .board = QCA9377_HW_1_0_BOARD_DATA_FILE, .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, @@ -634,13 +634,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9377 hw1.1 sdio", .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 19, + .led_pin = 0, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, .cal_data_len = 8124, .fw = { .dir = QCA9377_HW_1_0_FW_DIR, - .board = QCA9377_HW_1_0_BOARD_DATA_FILE, .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, @@ -668,6 +668,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca4019 hw1.0", .patch_load_addr = QCA4019_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .led_pin = 0, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, .otp_exe_param = 0x0010000, .continuous_frag_desc = true, @@ -680,7 +681,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .cal_data_len = 12064, .fw = { .dir = QCA4019_HW_1_0_FW_DIR, - .board = QCA4019_HW_1_0_BOARD_DATA_FILE, .board_size = QCA4019_BOARD_DATA_SZ, .board_ext_size = QCA4019_BOARD_EXT_DATA_SZ, }, @@ -714,12 +714,15 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dev_id = 0, .bus = ATH10K_BUS_SNOC, .name = "wcn3990 hw1.0", + .led_pin = 0, .continuous_frag_desc = true, .tx_chain_mask = 0x7, .rx_chain_mask = 0x7, .max_spatial_stream = 4, .fw = { .dir = WCN3990_HW_1_0_FW_DIR, + .board_size = WCN3990_BOARD_DATA_SZ, + .board_ext_size = WCN3990_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, .rx_desc_ops = &wcn3990_rx_desc_ops, @@ -942,11 +945,20 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, if (dir == NULL) dir = "."; + if (ar->board_name) { + snprintf(filename, sizeof(filename), "%s/%s/%s", + dir, ar->board_name, file); + ret = firmware_request_nowarn(&fw, filename, ar->dev); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n", + filename, ret); + if (!ret) + return fw; + } + snprintf(filename, sizeof(filename), "%s/%s", dir, file); ret = firmware_request_nowarn(&fw, filename, ar->dev); ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n", filename, ret); - if (ret) return ERR_PTR(ret); @@ -1151,9 +1163,12 @@ int ath10k_core_check_dt(struct ath10k *ar) if (!node) return -ENOENT; - of_property_read_string(node, "qcom,ath10k-calibration-variant", + of_property_read_string(node, "qcom,calibration-variant", &variant); if (!variant) + of_property_read_string(node, "qcom,ath10k-calibration-variant", + &variant); + if (!variant) return -ENODATA; if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0) @@ -1185,7 +1200,7 @@ static int ath10k_download_fw(struct ath10k *ar) } ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot uploading firmware image %pK len %d\n", + "boot uploading firmware image %p len %d\n", data, data_len); /* Check if device supports to download firmware via @@ -1288,11 +1303,6 @@ static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type) char boardname[100]; if (bd_ie_type == ATH10K_BD_IE_BOARD) { - if (!ar->hw_params.fw.board) { - ath10k_err(ar, "failed to find board file fw entry\n"); - return -EINVAL; - } - scnprintf(boardname, sizeof(boardname), "board-%s-%s.bin", ath10k_bus_str(ar->hif.bus), dev_name(ar->dev)); @@ -1302,7 +1312,7 @@ static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type) if (IS_ERR(ar->normal_mode_fw.board)) { fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.board); + ATH10K_BOARD_DATA_FILE); ar->normal_mode_fw.board = fw; } @@ -1312,13 +1322,8 @@ static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type) ar->normal_mode_fw.board_data = ar->normal_mode_fw.board->data; ar->normal_mode_fw.board_len = ar->normal_mode_fw.board->size; } else if (bd_ie_type == ATH10K_BD_IE_BOARD_EXT) { - if (!ar->hw_params.fw.eboard) { - ath10k_err(ar, "failed to find eboard file fw entry\n"); - return -EINVAL; - } - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.eboard); + ATH10K_EBOARD_DATA_FILE); ar->normal_mode_fw.ext_board = fw; if (IS_ERR(ar->normal_mode_fw.ext_board)) return PTR_ERR(ar->normal_mode_fw.ext_board); @@ -1821,7 +1826,7 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) if (!ar->running_fw->fw_file.otp_data || !ar->running_fw->fw_file.otp_len) { - ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %pK otp_len %zd)!\n", + ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n", ar->running_fw->fw_file.otp_data, ar->running_fw->fw_file.otp_len); return 0; @@ -2257,7 +2262,9 @@ static int ath10k_core_pre_cal_download(struct ath10k *ar) "boot did not find a pre calibration file, try DT next: %d\n", ret); - ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); + ret = ath10k_download_cal_dt(ar, "qcom,pre-calibration-data"); + if (ret == -ENOENT) + ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); if (ret) { ath10k_dbg(ar, ATH10K_DBG_BOOT, "unable to load pre cal data from DT: %d\n", ret); @@ -2335,7 +2342,9 @@ static int ath10k_download_cal_data(struct ath10k *ar) "boot did not find a calibration file, try DT next: %d\n", ret); - ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data"); + ret = ath10k_download_cal_dt(ar, "qcom,calibration-data"); + if (ret == -ENOENT) + ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data"); if (ret == 0) { ar->cal_mode = ATH10K_CAL_MODE_DT; goto done; @@ -3239,6 +3248,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err_hif_stop; } + status = ath10k_leds_start(ar); + if (status) + goto err_hif_stop; + return 0; err_hif_stop: @@ -3497,9 +3510,18 @@ static void ath10k_core_register_work(struct work_struct *work) goto err_spectral_destroy; } + status = ath10k_leds_register(ar); + if (status) { + ath10k_err(ar, "could not register leds: %d\n", + status); + goto err_thermal_unregister; + } + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); return; +err_thermal_unregister: + ath10k_thermal_unregister(ar); err_spectral_destroy: ath10k_spectral_destroy(ar); err_debug_destroy: @@ -3535,6 +3557,8 @@ void ath10k_core_unregister(struct ath10k *ar) if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) return; + ath10k_leds_unregister(ar); + ath10k_thermal_unregister(ar); /* Stop spectral before unregistering from mac80211 to remove the * relayfs debugfs file cleanly. Otherwise the parent debugfs tree @@ -3673,11 +3697,13 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, INIT_WORK(&ar->set_coverage_class_work, ath10k_core_set_coverage_class_work); - init_dummy_netdev(&ar->napi_dev); + ar->napi_dev = alloc_netdev_dummy(0); + if (!ar->napi_dev) + goto err_free_tx_complete; ret = ath10k_coredump_create(ar); if (ret) - goto err_free_tx_complete; + goto err_free_netdev; ret = ath10k_debug_create(ar); if (ret) @@ -3687,6 +3713,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, err_free_coredump: ath10k_coredump_destroy(ar); +err_free_netdev: + free_netdev(ar->napi_dev); err_free_tx_complete: destroy_workqueue(ar->workqueue_tx_complete); err_free_aux_wq: @@ -3708,6 +3736,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_tx_complete); + free_netdev(ar->napi_dev); ath10k_debug_destroy(ar); ath10k_coredump_destroy(ar); ath10k_htt_tx_destroy(&ar->htt); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index c110d15528bd..446dca74f06a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -15,6 +15,7 @@ #include <linux/pci.h> #include <linux/uuid.h> #include <linux/time.h> +#include <linux/leds.h> #include "htt.h" #include "htc.h" @@ -1081,6 +1082,8 @@ struct ath10k { */ const struct ath10k_fw_components *running_fw; + const char *board_name; + const struct firmware *pre_cal_file; const struct firmware *cal_file; @@ -1257,6 +1260,13 @@ struct ath10k { } testmode; struct { + struct gpio_led wifi_led; + struct led_classdev cdev; + char label[48]; + u32 gpio_state_pin; + } leds; + + struct { /* protected by data_lock */ u32 rx_crc_err_drop; u32 fw_crash_counter; @@ -1269,7 +1279,7 @@ struct ath10k { struct ath10k_per_peer_tx_stats peer_tx_stats; /* NAPI */ - struct net_device napi_dev; + struct net_device *napi_dev; struct napi_struct napi; struct work_struct set_coverage_class_work; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index b93a64bf8190..a0c1afeda4dd 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -1751,7 +1751,7 @@ void ath10k_debug_stop(struct ath10k *ar) /* Must not use _sync to avoid deadlock, we do that in * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid - * warning from del_timer(). + * warning from timer_delete(). */ if (ar->debug.htt_stats_mask != 0) cancel_delayed_work(&ar->debug.htt_stats_dwork); @@ -1774,7 +1774,7 @@ static ssize_t ath10k_write_simulate_radar(struct file *file, if (!arvif->is_started) return -EINVAL; - ieee80211_radar_detected(ar->hw); + ieee80211_radar_detected(ar->hw, NULL); return count; } diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index 394bf3c32abf..0f6de862c3a9 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -439,7 +439,7 @@ ath10k_dbg_sta_write_peer_debug_trigger(struct file *file, } out: mutex_unlock(&ar->conf_mutex); - return count; + return ret ?: count; } static const struct file_operations fops_peer_debug_trigger = { diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index a6e21ce90bad..2da08dfebd3e 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -34,7 +34,7 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) skb_cb = ATH10K_SKB_CB(skb); memset(skb_cb, 0, sizeof(*skb_cb)); - ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %pK\n", __func__, skb); + ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb); return skb; } @@ -54,7 +54,7 @@ void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, struct ath10k *ar = ep->htc->ar; struct ath10k_htc_hdr *hdr; - ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__, + ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, ep->eid, skb); /* A corner case where the copy completion is reaching to host but still @@ -515,7 +515,7 @@ void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb) /* zero length packet with trailer data, just drop these */ goto out; - ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %pK\n", + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n", eid, skb); ep->ep_ops.ep_rx_complete(ar, skb); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 7d28ae5453cf..fb0d5d4cae3a 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -257,7 +257,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) static void ath10k_htt_rx_ring_refill_retry(struct timer_list *t) { - struct ath10k_htt *htt = from_timer(htt, t, rx_ring.refill_retry_timer); + struct ath10k_htt *htt = timer_container_of(htt, t, + rx_ring.refill_retry_timer); ath10k_htt_rx_msdu_buff_replenish(htt); } @@ -287,7 +288,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt) if (htt->ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) return; - del_timer_sync(&htt->rx_ring.refill_retry_timer); + timer_delete_sync(&htt->rx_ring.refill_retry_timer); skb_queue_purge(&htt->rx_msdus_q); skb_queue_purge(&htt->rx_in_ord_compl_q); @@ -1373,7 +1374,7 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) } ath10k_dbg(ar, ATH10K_DBG_DATA, - "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, skb->len, ieee80211_get_SA(hdr), diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 8fafe096adff..84b35a22fc23 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -212,40 +212,40 @@ const struct ath10k_hw_regs wcn3990_regs = { .pcie_intr_fw_mask = 0x00100000, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = { .msb = 0x00000010, .lsb = 0x00000010, .mask = GENMASK(17, 17), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = { .msb = 0x00000012, .lsb = 0x00000012, .mask = GENMASK(18, 18), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = { .msb = 0x00000000, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = { +static const struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = { .addr = 0x00000018, .src_ring = &wcn3990_src_ring, .dst_ring = &wcn3990_dst_ring, .dmax = &wcn3990_dmax, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = { .mask = GENMASK(0, 0), }; -static struct ath10k_hw_ce_host_ie wcn3990_host_ie = { +static const struct ath10k_hw_ce_host_ie wcn3990_host_ie = { .copy_complete = &wcn3990_host_ie_cc, }; -static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { +static const struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { .dstr_lmask = 0x00000010, .dstr_hmask = 0x00000008, .srcr_lmask = 0x00000004, @@ -255,7 +255,7 @@ static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { .addr = 0x00000030, }; -static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { +static const struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { .axi_err = 0x00000100, .dstr_add_err = 0x00000200, .srcr_len_err = 0x00000100, @@ -266,19 +266,19 @@ static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { .addr = 0x00000038, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = { .msb = 0x00000000, .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { .addr = 0x0000004c, .low_rst = 0x00000000, .high_rst = 0x00000000, @@ -286,18 +286,18 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { .wm_high = &wcn3990_src_wm_high, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = { .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { .addr = 0x00000050, .low_rst = 0x00000000, .high_rst = 0x00000000, @@ -305,7 +305,7 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { .wm_high = &wcn3990_dst_wm_high, }; -static struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = { +static const struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = { .shift = 19, .mask = 0x00080000, .enable = 0x00000000, @@ -344,25 +344,25 @@ const struct ath10k_hw_values wcn3990_values = { .ce_desc_meta_data_lsb = 4, }; -static struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { +static const struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { .msb = 0x00000010, .lsb = 0x00000010, .mask = GENMASK(16, 16), }; -static struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = { .msb = 0x00000011, .lsb = 0x00000011, .mask = GENMASK(17, 17), }; -static struct ath10k_hw_ce_regs_addr_map qcax_dmax = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dmax = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { +static const struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { .addr = 0x00000010, .hw_mask = 0x0007ffff, .sw_mask = 0x0007ffff, @@ -375,31 +375,31 @@ static struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { .dmax = &qcax_dmax, }; -static struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = { +static const struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = { .msb = 0x00000003, .lsb = 0x00000003, .mask = GENMASK(3, 3), }; -static struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = { +static const struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = { .msb = 0x00000000, .mask = GENMASK(0, 0), .status_reset = 0x00000000, .status = &qcax_cmd_halt_status, }; -static struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = { +static const struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = { .msb = 0x00000000, .lsb = 0x00000000, .mask = GENMASK(0, 0), }; -static struct ath10k_hw_ce_host_ie qcax_host_ie = { +static const struct ath10k_hw_ce_host_ie qcax_host_ie = { .copy_complete_reset = 0x00000000, .copy_complete = &qcax_host_ie_cc, }; -static struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { +static const struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { .dstr_lmask = 0x00000010, .dstr_hmask = 0x00000008, .srcr_lmask = 0x00000004, @@ -409,7 +409,7 @@ static struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { .addr = 0x00000030, }; -static struct ath10k_hw_ce_misc_regs qcax_misc_reg = { +static const struct ath10k_hw_ce_misc_regs qcax_misc_reg = { .axi_err = 0x00000400, .dstr_add_err = 0x00000200, .srcr_len_err = 0x00000100, @@ -420,19 +420,19 @@ static struct ath10k_hw_ce_misc_regs qcax_misc_reg = { .addr = 0x00000038, }; -static struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = { .msb = 0x0000001f, .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { .addr = 0x0000004c, .low_rst = 0x00000000, .high_rst = 0x00000000, @@ -440,18 +440,18 @@ static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { .wm_high = &qcax_src_wm_high, }; -static struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = { .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = { .addr = 0x00000050, .low_rst = 0x00000000, .high_rst = 0x00000000, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 93c073091996..7ffa1fbe2874 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -39,14 +39,12 @@ enum ath10k_bus { #define QCA988X_HW_2_0_VERSION 0x4100016c #define QCA988X_HW_2_0_CHIP_ID_REV 0x2 #define QCA988X_HW_2_0_FW_DIR ATH10K_FW_DIR "/QCA988X/hw2.0" -#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 /* QCA9887 1.0 definitions */ #define QCA9887_HW_1_0_VERSION 0x4100016d #define QCA9887_HW_1_0_CHIP_ID_REV 0 #define QCA9887_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA9887/hw1.0" -#define QCA9887_HW_1_0_BOARD_DATA_FILE "board.bin" #define QCA9887_HW_1_0_PATCH_LOAD_ADDR 0x1234 /* QCA6174 target BMI version signatures */ @@ -85,11 +83,9 @@ enum qca9377_chip_id_rev { }; #define QCA6174_HW_2_1_FW_DIR ATH10K_FW_DIR "/QCA6174/hw2.1" -#define QCA6174_HW_2_1_BOARD_DATA_FILE "board.bin" #define QCA6174_HW_2_1_PATCH_LOAD_ADDR 0x1234 #define QCA6174_HW_3_0_FW_DIR ATH10K_FW_DIR "/QCA6174/hw3.0" -#define QCA6174_HW_3_0_BOARD_DATA_FILE "board.bin" #define QCA6174_HW_3_0_PATCH_LOAD_ADDR 0x1234 /* QCA99X0 1.0 definitions (unsupported) */ @@ -99,7 +95,6 @@ enum qca9377_chip_id_rev { #define QCA99X0_HW_2_0_DEV_VERSION 0x01000000 #define QCA99X0_HW_2_0_CHIP_ID_REV 0x1 #define QCA99X0_HW_2_0_FW_DIR ATH10K_FW_DIR "/QCA99X0/hw2.0" -#define QCA99X0_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA99X0_HW_2_0_PATCH_LOAD_ADDR 0x1234 /* QCA9984 1.0 defines */ @@ -107,8 +102,6 @@ enum qca9377_chip_id_rev { #define QCA9984_HW_DEV_TYPE 0xa #define QCA9984_HW_1_0_CHIP_ID_REV 0x0 #define QCA9984_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA9984/hw1.0" -#define QCA9984_HW_1_0_BOARD_DATA_FILE "board.bin" -#define QCA9984_HW_1_0_EBOARD_DATA_FILE "eboard.bin" #define QCA9984_HW_1_0_PATCH_LOAD_ADDR 0x1234 /* QCA9888 2.0 defines */ @@ -116,18 +109,15 @@ enum qca9377_chip_id_rev { #define QCA9888_HW_DEV_TYPE 0xc #define QCA9888_HW_2_0_CHIP_ID_REV 0x0 #define QCA9888_HW_2_0_FW_DIR ATH10K_FW_DIR "/QCA9888/hw2.0" -#define QCA9888_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA9888_HW_2_0_PATCH_LOAD_ADDR 0x1234 /* QCA9377 1.0 definitions */ #define QCA9377_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA9377/hw1.0" -#define QCA9377_HW_1_0_BOARD_DATA_FILE "board.bin" #define QCA9377_HW_1_0_PATCH_LOAD_ADDR 0x1234 /* QCA4019 1.0 definitions */ #define QCA4019_HW_1_0_DEV_VERSION 0x01000000 #define QCA4019_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA4019/hw1.0" -#define QCA4019_HW_1_0_BOARD_DATA_FILE "board.bin" #define QCA4019_HW_1_0_PATCH_LOAD_ADDR 0x1234 /* WCN3990 1.0 definitions */ @@ -159,7 +149,9 @@ enum qca9377_chip_id_rev { #define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" #define ATH10K_BOARD_MAGIC "QCA-ATH10K-BOARD" +#define ATH10K_BOARD_DATA_FILE "board.bin" #define ATH10K_BOARD_API2_FILE "board-2.bin" +#define ATH10K_EBOARD_DATA_FILE "eboard.bin" #define REG_DUMP_COUNT_QCA988X 60 @@ -297,19 +289,22 @@ struct ath10k_hw_ce_ctrl1 { u32 sw_wr_mask; u32 reset_mask; u32 reset; - struct ath10k_hw_ce_regs_addr_map *src_ring; - struct ath10k_hw_ce_regs_addr_map *dst_ring; - struct ath10k_hw_ce_regs_addr_map *dmax; }; + const struct ath10k_hw_ce_regs_addr_map *src_ring; + const struct ath10k_hw_ce_regs_addr_map *dst_ring; + const struct ath10k_hw_ce_regs_addr_map *dmax; +}; struct ath10k_hw_ce_cmd_halt { u32 status_reset; u32 msb; u32 mask; - struct ath10k_hw_ce_regs_addr_map *status; }; + const struct ath10k_hw_ce_regs_addr_map *status; +}; struct ath10k_hw_ce_host_ie { u32 copy_complete_reset; - struct ath10k_hw_ce_regs_addr_map *copy_complete; }; + const struct ath10k_hw_ce_regs_addr_map *copy_complete; +}; struct ath10k_hw_ce_host_wm_regs { u32 dstr_lmask; @@ -336,8 +331,9 @@ struct ath10k_hw_ce_dst_src_wm_regs { u32 addr; u32 low_rst; u32 high_rst; - struct ath10k_hw_ce_regs_addr_map *wm_low; - struct ath10k_hw_ce_regs_addr_map *wm_high; }; + const struct ath10k_hw_ce_regs_addr_map *wm_low; + const struct ath10k_hw_ce_regs_addr_map *wm_high; +}; struct ath10k_hw_ce_ctrl1_upd { u32 shift; @@ -363,14 +359,14 @@ struct ath10k_hw_ce_regs { u32 ce_rri_low; u32 ce_rri_high; u32 host_ie_addr; - struct ath10k_hw_ce_host_wm_regs *wm_regs; - struct ath10k_hw_ce_misc_regs *misc_regs; - struct ath10k_hw_ce_ctrl1 *ctrl1_regs; - struct ath10k_hw_ce_cmd_halt *cmd_halt; - struct ath10k_hw_ce_host_ie *host_ie; - struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr; - struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; - struct ath10k_hw_ce_ctrl1_upd *upd; + const struct ath10k_hw_ce_host_wm_regs *wm_regs; + const struct ath10k_hw_ce_misc_regs *misc_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl1_regs; + const struct ath10k_hw_ce_cmd_halt *cmd_halt; + const struct ath10k_hw_ce_host_ie *host_ie; + const struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr; + const struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; + const struct ath10k_hw_ce_ctrl1_upd *upd; }; struct ath10k_hw_values { @@ -520,6 +516,7 @@ struct ath10k_hw_params { const char *name; u32 patch_load_addr; int uart_pin; + int led_pin; u32 otp_exe_param; /* Type of hw cycle counter wraparound logic, for more info @@ -553,9 +550,7 @@ struct ath10k_hw_params { struct ath10k_hw_params_fw { const char *dir; - const char *board; size_t board_size; - const char *eboard; size_t ext_board_size; size_t board_ext_size; } fw; diff --git a/drivers/net/wireless/ath/ath10k/leds.c b/drivers/net/wireless/ath/ath10k/leds.c new file mode 100644 index 000000000000..9b1d04eb4265 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/leds.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018 Sebastian Gottschall <s.gottschall@dd-wrt.com> + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + */ + +#include <linux/leds.h> + +#include "core.h" +#include "wmi.h" +#include "wmi-ops.h" + +#include "leds.h" + +static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath10k *ar = container_of(led_cdev, struct ath10k, + leds.cdev); + struct gpio_led *led = &ar->leds.wifi_led; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH10K_STATE_ON) + goto out; + + ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low; + ath10k_wmi_gpio_output(ar, led->gpio, ar->leds.gpio_state_pin); + +out: + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +int ath10k_leds_start(struct ath10k *ar) +{ + if (ar->hw_params.led_pin == 0) + /* leds not supported */ + return 0; + + /* under some circumstances, the gpio pin gets reconfigured + * to default state by the firmware, so we need to + * reconfigure it this behaviour has only ben seen on + * QCA9984 and QCA99XX devices so far + */ + ath10k_wmi_gpio_config(ar, ar->hw_params.led_pin, 0, + WMI_GPIO_PULL_NONE, WMI_GPIO_INTTYPE_DISABLE); + ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, 1); + + return 0; +} + +int ath10k_leds_register(struct ath10k *ar) +{ + int ret; + + if (ar->hw_params.led_pin == 0) + /* leds not supported */ + return 0; + + snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s", + wiphy_name(ar->hw->wiphy)); + ar->leds.wifi_led.active_low = 1; + ar->leds.wifi_led.gpio = ar->hw_params.led_pin; + ar->leds.wifi_led.name = ar->leds.label; + ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP; + + ar->leds.cdev.name = ar->leds.label; + ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking; + ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger; + + ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev); + if (ret) + return ret; + + return 0; +} + +void ath10k_leds_unregister(struct ath10k *ar) +{ + if (ar->hw_params.led_pin == 0) + /* leds not supported */ + return; + + led_classdev_unregister(&ar->leds.cdev); +} + diff --git a/drivers/net/wireless/ath/ath10k/leds.h b/drivers/net/wireless/ath/ath10k/leds.h new file mode 100644 index 000000000000..56325b0875e5 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/leds.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018 Sebastian Gottschall <s.gottschall@dd-wrt.com> + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + */ + +#ifndef _LEDS_H_ +#define _LEDS_H_ + +#include "core.h" + +#ifdef CONFIG_ATH10K_LEDS +void ath10k_leds_unregister(struct ath10k *ar); +int ath10k_leds_start(struct ath10k *ar); +int ath10k_leds_register(struct ath10k *ar); +#else +static inline void ath10k_leds_unregister(struct ath10k *ar) +{ +} + +static inline int ath10k_leds_start(struct ath10k *ar) +{ + return 0; +} + +static inline int ath10k_leds_register(struct ath10k *ar) +{ + return 0; +} + +#endif +#endif /* _LEDS_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index e322b528baaf..07fe05384cdf 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4,6 +4,7 @@ * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "mac.h" @@ -25,6 +26,7 @@ #include "wmi-tlv.h" #include "wmi-ops.h" #include "wow.h" +#include "leds.h" /*********/ /* Rates */ @@ -874,7 +876,7 @@ static void ath10k_peer_map_cleanup(struct ath10k *ar, struct ath10k_peer *peer) */ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) { if (ar->peer_map[i] == peer) { - ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %pK idx %d)\n", + ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %p idx %d)\n", peer->addr, peer, i); ar->peer_map[i] = NULL; } @@ -1021,6 +1023,26 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) return ar->last_wmi_vdev_start_status; } +static inline int ath10k_vdev_delete_sync(struct ath10k *ar) +{ + unsigned long time_left; + + lockdep_assert_held(&ar->conf_mutex); + + if (!test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) + return 0; + + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return -ESHUTDOWN; + + time_left = wait_for_completion_timeout(&ar->vdev_delete_done, + ATH10K_VDEV_DELETE_TIMEOUT_HZ); + if (time_left == 0) + return -ETIMEDOUT; + + return 0; +} + static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) { struct cfg80211_chan_def *chandef = NULL; @@ -1436,7 +1458,7 @@ static void ath10k_recalc_radar_detection(struct ath10k *ar) * by indicating that radar was detected. */ ath10k_warn(ar, "failed to start CAC: %d\n", ret); - ieee80211_radar_detected(ar->hw); + ieee80211_radar_detected(ar->hw, NULL); } } @@ -4062,7 +4084,7 @@ static int ath10k_mac_tx(struct ath10k *ar, if (!noque_offchan && info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { if (!ath10k_mac_tx_frm_has_freq(ar)) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %pK len %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %p len %d\n", skb, skb->len); skb_queue_tail(&ar->offchan_tx_queue, skb); @@ -4125,7 +4147,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) mutex_lock(&ar->conf_mutex); - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK len %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %p len %d\n", skb, skb->len); hdr = (struct ieee80211_hdr *)skb->data; @@ -4180,7 +4202,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) time_left = wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ); if (time_left == 0) - ath10k_warn(ar, "timed out waiting for offchannel skb %pK, len: %d\n", + ath10k_warn(ar, "timed out waiting for offchannel skb %p, len: %d\n", skb, skb->len); if (!peer && tmp_peer_created) { @@ -5362,7 +5384,7 @@ err: return ret; } -static void ath10k_stop(struct ieee80211_hw *hw) +static void ath10k_stop(struct ieee80211_hw *hw, bool suspend) { struct ath10k *ar = hw->priv; u32 opt; @@ -5899,7 +5921,6 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; struct ath10k_peer *peer; - unsigned long time_left; int ret; int i; @@ -5939,13 +5960,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n", arvif->vdev_id, ret); - if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) { - time_left = wait_for_completion_timeout(&ar->vdev_delete_done, - ATH10K_VDEV_DELETE_TIMEOUT_HZ); - if (time_left == 0) { - ath10k_warn(ar, "Timeout in receiving vdev delete response\n"); - goto out; - } + ret = ath10k_vdev_delete_sync(ar); + if (ret) { + ath10k_warn(ar, "Error in receiving vdev delete response: %d\n", ret); + goto out; } /* Some firmware revisions don't notify host about self-peer removal @@ -6368,7 +6386,7 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; struct cfg80211_scan_request *req = &hw_req->req; - struct wmi_start_scan_arg arg; + struct wmi_start_scan_arg *arg = NULL; int ret = 0; int i; u32 scan_timeout; @@ -6401,56 +6419,61 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - memset(&arg, 0, sizeof(arg)); - ath10k_wmi_start_scan_init(ar, &arg); - arg.vdev_id = arvif->vdev_id; - arg.scan_id = ATH10K_SCAN_ID; + arg = kzalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) { + ret = -ENOMEM; + goto exit; + } + + ath10k_wmi_start_scan_init(ar, arg); + arg->vdev_id = arvif->vdev_id; + arg->scan_id = ATH10K_SCAN_ID; if (req->ie_len) { - arg.ie_len = req->ie_len; - memcpy(arg.ie, req->ie, arg.ie_len); + arg->ie_len = req->ie_len; + memcpy(arg->ie, req->ie, arg->ie_len); } if (req->n_ssids) { - arg.n_ssids = req->n_ssids; - for (i = 0; i < arg.n_ssids; i++) { - arg.ssids[i].len = req->ssids[i].ssid_len; - arg.ssids[i].ssid = req->ssids[i].ssid; + arg->n_ssids = req->n_ssids; + for (i = 0; i < arg->n_ssids; i++) { + arg->ssids[i].len = req->ssids[i].ssid_len; + arg->ssids[i].ssid = req->ssids[i].ssid; } } else { - arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + arg->scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; } if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - arg.scan_ctrl_flags |= WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ; - ether_addr_copy(arg.mac_addr.addr, req->mac_addr); - ether_addr_copy(arg.mac_mask.addr, req->mac_addr_mask); + arg->scan_ctrl_flags |= WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ; + ether_addr_copy(arg->mac_addr.addr, req->mac_addr); + ether_addr_copy(arg->mac_mask.addr, req->mac_addr_mask); } if (req->n_channels) { - arg.n_channels = req->n_channels; - for (i = 0; i < arg.n_channels; i++) - arg.channels[i] = req->channels[i]->center_freq; + arg->n_channels = req->n_channels; + for (i = 0; i < arg->n_channels; i++) + arg->channels[i] = req->channels[i]->center_freq; } /* if duration is set, default dwell times will be overwritten */ if (req->duration) { - arg.dwell_time_active = req->duration; - arg.dwell_time_passive = req->duration; - arg.burst_duration_ms = req->duration; + arg->dwell_time_active = req->duration; + arg->dwell_time_passive = req->duration; + arg->burst_duration_ms = req->duration; - scan_timeout = min_t(u32, arg.max_rest_time * - (arg.n_channels - 1) + (req->duration + + scan_timeout = min_t(u32, arg->max_rest_time * + (arg->n_channels - 1) + (req->duration + ATH10K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD) * - arg.n_channels, arg.max_scan_time); + arg->n_channels, arg->max_scan_time); } else { - scan_timeout = arg.max_scan_time; + scan_timeout = arg->max_scan_time; } /* Add a 200ms margin to account for event/command processing */ scan_timeout += 200; - ret = ath10k_start_scan(ar, &arg); + ret = ath10k_start_scan(ar, arg); if (ret) { ath10k_warn(ar, "failed to start hw scan: %d\n", ret); spin_lock_bh(&ar->data_lock); @@ -6462,6 +6485,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, msecs_to_jiffies(scan_timeout)); exit: + kfree(arg); + mutex_unlock(&ar->conf_mutex); return ret; } @@ -7596,7 +7621,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, * Existing station deletion. */ ath10k_dbg(ar, ATH10K_DBG_STA, - "mac vdev %d peer delete %pM sta %pK (sta gone)\n", + "mac vdev %d peer delete %pM sta %p (sta gone)\n", arvif->vdev_id, sta->addr, sta); if (sta->tdls) { @@ -7623,7 +7648,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, continue; if (peer->sta == sta) { - ath10k_warn(ar, "found sta peer %pM (ptr %pK id %d) entry on vdev %i after it was supposedly removed\n", + ath10k_warn(ar, "found sta peer %pM (ptr %p id %d) entry on vdev %i after it was supposedly removed\n", sta->addr, peer, i, arvif->vdev_id); peer->sta = NULL; @@ -7898,7 +7923,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; - struct wmi_start_scan_arg arg; + struct wmi_start_scan_arg *arg = NULL; int ret = 0; u32 scan_time_msec; @@ -7935,20 +7960,25 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2; - memset(&arg, 0, sizeof(arg)); - ath10k_wmi_start_scan_init(ar, &arg); - arg.vdev_id = arvif->vdev_id; - arg.scan_id = ATH10K_SCAN_ID; - arg.n_channels = 1; - arg.channels[0] = chan->center_freq; - arg.dwell_time_active = scan_time_msec; - arg.dwell_time_passive = scan_time_msec; - arg.max_scan_time = scan_time_msec; - arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; - arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; - arg.burst_duration_ms = duration; - - ret = ath10k_start_scan(ar, &arg); + arg = kzalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) { + ret = -ENOMEM; + goto exit; + } + + ath10k_wmi_start_scan_init(ar, arg); + arg->vdev_id = arvif->vdev_id; + arg->scan_id = ATH10K_SCAN_ID; + arg->n_channels = 1; + arg->channels[0] = chan->center_freq; + arg->dwell_time_active = scan_time_msec; + arg->dwell_time_passive = scan_time_msec; + arg->max_scan_time = scan_time_msec; + arg->scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + arg->scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; + arg->burst_duration_ms = duration; + + ret = ath10k_start_scan(ar, arg); if (ret) { ath10k_warn(ar, "failed to start roc scan: %d\n", ret); spin_lock_bh(&ar->data_lock); @@ -7974,6 +8004,8 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, ret = 0; exit: + kfree(arg); + mutex_unlock(&ar->conf_mutex); return ret; } @@ -8506,9 +8538,10 @@ exit: static void ath10k_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ieee80211_link_sta *link_sta, u32 changed) { + struct ieee80211_sta *sta = link_sta->sta; struct ath10k *ar = hw->priv; struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; @@ -8795,7 +8828,7 @@ ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx add freq %u width %d ptr %pK\n", + "mac chanctx add freq %u width %d ptr %p\n", ctx->def.chan->center_freq, ctx->def.width, ctx); mutex_lock(&ar->conf_mutex); @@ -8819,7 +8852,7 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx remove freq %u width %d ptr %pK\n", + "mac chanctx remove freq %u width %d ptr %p\n", ctx->def.chan->center_freq, ctx->def.width, ctx); mutex_lock(&ar->conf_mutex); @@ -8884,7 +8917,7 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx change freq %u width %d ptr %pK changed %x\n", + "mac chanctx change freq %u width %d ptr %p changed %x\n", ctx->def.chan->center_freq, ctx->def.width, ctx, changed); /* This shouldn't really happen because channel switching should use @@ -8943,7 +8976,7 @@ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx assign ptr %pK vdev_id %i\n", + "mac chanctx assign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); if (WARN_ON(arvif->is_started)) { @@ -9023,7 +9056,7 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx unassign ptr %pK vdev_id %i\n", + "mac chanctx unassign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); WARN_ON(!arvif->is_started); @@ -9120,7 +9153,7 @@ static const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss1[ {6, {2633, 2925}, {1215, 1350}, {585, 650} }, {7, {2925, 3250}, {1350, 1500}, {650, 722} }, {8, {3510, 3900}, {1620, 1800}, {780, 867} }, - {9, {3900, 4333}, {1800, 2000}, {780, 867} } + {9, {3900, 4333}, {1800, 2000}, {865, 960} } }; /*MCS parameters with Nss = 2 */ @@ -9135,7 +9168,7 @@ static const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss2[ {6, {5265, 5850}, {2430, 2700}, {1170, 1300} }, {7, {5850, 6500}, {2700, 3000}, {1300, 1444} }, {8, {7020, 7800}, {3240, 3600}, {1560, 1733} }, - {9, {7800, 8667}, {3600, 4000}, {1560, 1733} } + {9, {7800, 8667}, {3600, 4000}, {1730, 1920} } }; static void ath10k_mac_get_rate_flags_ht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs, @@ -9449,7 +9482,7 @@ static const struct ieee80211_ops ath10k_ops = { .reconfig_complete = ath10k_reconfig_complete, .get_survey = ath10k_get_survey, .set_bitrate_mask = ath10k_mac_op_set_bitrate_mask, - .sta_rc_update = ath10k_sta_rc_update, + .link_sta_rc_update = ath10k_sta_rc_update, .offset_tsf = ath10k_offset_tsf, .ampdu_action = ath10k_ampdu_action, .get_et_sset_count = ath10k_debug_get_et_sset_count, diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 5c34b156b4ff..1e6d43285138 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -591,7 +591,7 @@ skip: static void ath10k_pci_ps_timer(struct timer_list *t) { - struct ath10k_pci *ar_pci = from_timer(ar_pci, t, ps_timer); + struct ath10k_pci *ar_pci = timer_container_of(ar_pci, t, ps_timer); struct ath10k *ar = ar_pci->ar; unsigned long flags; @@ -619,7 +619,7 @@ static void ath10k_pci_sleep_sync(struct ath10k *ar) return; } - del_timer_sync(&ar_pci->ps_timer); + timer_delete_sync(&ar_pci->ps_timer); spin_lock_irqsave(&ar_pci->ps_lock, flags); WARN_ON(ar_pci->ps_wake_refcount > 0); @@ -721,7 +721,7 @@ bool ath10k_pci_irq_pending(struct ath10k *ar) return false; } -void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) +void ath10k_pci_disable_and_clear_intx_irq(struct ath10k *ar) { /* IMPORTANT: INTR_CLR register has to be set after * INTR_ENABLE is set to 0, otherwise interrupt can not be @@ -739,7 +739,7 @@ void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) PCIE_INTR_ENABLE_ADDRESS); } -void ath10k_pci_enable_legacy_irq(struct ath10k *ar) +void ath10k_pci_enable_intx_irq(struct ath10k *ar) { ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, @@ -844,7 +844,8 @@ void ath10k_pci_rx_post(struct ath10k *ar) void ath10k_pci_rx_replenish_retry(struct timer_list *t) { - struct ath10k_pci *ar_pci = from_timer(ar_pci, t, rx_post_retry); + struct ath10k_pci *ar_pci = timer_container_of(ar_pci, t, + rx_post_retry); struct ath10k *ar = ar_pci->ar; ath10k_pci_rx_post(ar); @@ -1817,7 +1818,7 @@ static void ath10k_pci_rx_retry_sync(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - del_timer_sync(&ar_pci->rx_post_retry); + timer_delete_sync(&ar_pci->rx_post_retry); } int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, @@ -1935,7 +1936,7 @@ static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar) static void ath10k_pci_irq_disable(struct ath10k *ar) { ath10k_ce_disable_interrupts(ar); - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); } @@ -1949,7 +1950,7 @@ static void ath10k_pci_irq_sync(struct ath10k *ar) static void ath10k_pci_irq_enable(struct ath10k *ar) { ath10k_ce_enable_interrupts(ar); - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); ath10k_pci_irq_msi_fw_unmask(ar); } @@ -3111,11 +3112,11 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) return IRQ_NONE; } - if ((ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY) && + if ((ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_INTX) && !ath10k_pci_irq_pending(ar)) return IRQ_NONE; - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); napi_schedule(&ar->napi); @@ -3152,7 +3153,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget) napi_schedule(ctx); goto out; } - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); ath10k_pci_irq_msi_fw_unmask(ar); } @@ -3177,7 +3178,7 @@ static int ath10k_pci_request_irq_msi(struct ath10k *ar) return 0; } -static int ath10k_pci_request_irq_legacy(struct ath10k *ar) +static int ath10k_pci_request_irq_intx(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; @@ -3199,8 +3200,8 @@ static int ath10k_pci_request_irq(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); switch (ar_pci->oper_irq_mode) { - case ATH10K_PCI_IRQ_LEGACY: - return ath10k_pci_request_irq_legacy(ar); + case ATH10K_PCI_IRQ_INTX: + return ath10k_pci_request_irq_intx(ar); case ATH10K_PCI_IRQ_MSI: return ath10k_pci_request_irq_msi(ar); default: @@ -3217,7 +3218,7 @@ static void ath10k_pci_free_irq(struct ath10k *ar) void ath10k_pci_init_napi(struct ath10k *ar) { - netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll); + netif_napi_add(ar->napi_dev, &ar->napi, ath10k_pci_napi_poll); } static int ath10k_pci_init_irq(struct ath10k *ar) @@ -3232,7 +3233,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) ath10k_pci_irq_mode); /* Try MSI */ - if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_LEGACY) { + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_INTX) { ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_MSI; ret = pci_enable_msi(ar_pci->pdev); if (ret == 0) @@ -3250,7 +3251,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) * For now, fix the race by repeating the write in below * synchronization checking. */ - ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_LEGACY; + ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_INTX; ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); @@ -3258,7 +3259,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) return 0; } -static void ath10k_pci_deinit_irq_legacy(struct ath10k *ar) +static void ath10k_pci_deinit_irq_intx(struct ath10k *ar) { ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, 0); @@ -3269,8 +3270,8 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); switch (ar_pci->oper_irq_mode) { - case ATH10K_PCI_IRQ_LEGACY: - ath10k_pci_deinit_irq_legacy(ar); + case ATH10K_PCI_IRQ_INTX: + ath10k_pci_deinit_irq_intx(ar); break; default: pci_disable_msi(ar_pci->pdev); @@ -3307,14 +3308,14 @@ int ath10k_pci_wait_for_target_init(struct ath10k *ar) if (val & FW_IND_INITIALIZED) break; - if (ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY) + if (ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_INTX) /* Fix potential race by repeating CORE_BASE writes */ - ath10k_pci_enable_legacy_irq(ar); + ath10k_pci_enable_intx_irq(ar); mdelay(10); } while (time_before(jiffies, timeout)); - ath10k_pci_disable_and_clear_legacy_irq(ar); + ath10k_pci_disable_and_clear_intx_irq(ar); ath10k_pci_irq_msi_fw_mask(ar); if (val == 0xffffffff) { @@ -3411,7 +3412,7 @@ static int ath10k_pci_claim(struct ath10k *ar) goto err_region; } - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%pK\n", ar_pci->mem); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem); return 0; err_region: @@ -3826,28 +3827,28 @@ MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API2_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API3_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API5_FILE); -MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_BOARD_DATA_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA9887 1.0 firmware files */ MODULE_FIRMWARE(QCA9887_HW_1_0_FW_DIR "/" ATH10K_FW_API5_FILE); -MODULE_FIRMWARE(QCA9887_HW_1_0_FW_DIR "/" QCA9887_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA9887_HW_1_0_FW_DIR "/" ATH10K_BOARD_DATA_FILE); MODULE_FIRMWARE(QCA9887_HW_1_0_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA6174 2.1 firmware files */ MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_FW_API5_FILE); -MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" QCA6174_HW_2_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_BOARD_DATA_FILE); MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA6174 3.1 firmware files */ MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API5_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API6_FILE); -MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" QCA6174_HW_3_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_BOARD_DATA_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA9377 1.0 firmware files */ MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_FW_API6_FILE); MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_FW_API5_FILE); -MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" QCA9377_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 27bb4cf2dfea..4c3f536f2ea1 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -101,7 +101,7 @@ struct ath10k_pci_supp_chip { enum ath10k_pci_irq_mode { ATH10K_PCI_IRQ_AUTO = 0, - ATH10K_PCI_IRQ_LEGACY = 1, + ATH10K_PCI_IRQ_INTX = 1, ATH10K_PCI_IRQ_MSI = 2, }; @@ -243,9 +243,9 @@ int ath10k_pci_init_pipes(struct ath10k *ar); int ath10k_pci_init_config(struct ath10k *ar); void ath10k_pci_rx_post(struct ath10k *ar); void ath10k_pci_flush(struct ath10k *ar); -void ath10k_pci_enable_legacy_irq(struct ath10k *ar); +void ath10k_pci_enable_intx_irq(struct ath10k *ar); bool ath10k_pci_irq_pending(struct ath10k *ar); -void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar); +void ath10k_pci_disable_and_clear_intx_irq(struct ath10k *ar); void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar); int ath10k_pci_wait_for_target_init(struct ath10k *ar); int ath10k_pci_setup_resource(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 38e939f572a9..f1f33af0170a 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -1040,6 +1040,10 @@ static void ath10k_qmi_driver_event_work(struct work_struct *work) switch (event->type) { case ATH10K_QMI_EVENT_SERVER_ARRIVE: ath10k_qmi_event_server_arrive(qmi); + if (qmi->no_msa_ready_indicator) { + ath10k_info(ar, "qmi not waiting for msa_ready indicator"); + ath10k_qmi_event_msa_ready(qmi); + } break; case ATH10K_QMI_EVENT_SERVER_EXIT: ath10k_qmi_event_server_exit(qmi); @@ -1048,6 +1052,10 @@ static void ath10k_qmi_driver_event_work(struct work_struct *work) ath10k_qmi_event_fw_ready_ind(qmi); break; case ATH10K_QMI_EVENT_MSA_READY_IND: + if (qmi->no_msa_ready_indicator) { + ath10k_warn(ar, "qmi unexpected msa_ready indicator"); + break; + } ath10k_qmi_event_msa_ready(qmi); break; default: @@ -1077,6 +1085,9 @@ int ath10k_qmi_init(struct ath10k *ar, u32 msa_size) if (of_property_read_bool(dev->of_node, "qcom,msa-fixed-perm")) qmi->msa_fixed_perm = true; + if (of_property_read_bool(dev->of_node, "qcom,no-msa-ready-indicator")) + qmi->no_msa_ready_indicator = true; + ret = qmi_handle_init(&qmi->qmi_hdl, WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN, &ath10k_qmi_ops, qmi_msg_handler); diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h index 89464239fe96..0816eb4e4a18 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.h +++ b/drivers/net/wireless/ath/ath10k/qmi.h @@ -107,6 +107,7 @@ struct ath10k_qmi { char fw_build_timestamp[MAX_TIMESTAMP_LEN + 1]; struct ath10k_qmi_cal_data cal_data[MAX_NUM_CAL_V01]; bool msa_fixed_perm; + bool no_msa_ready_indicator; enum ath10k_qmi_state state; }; diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 0ab5433f6cf6..c06d50db40b8 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -3,7 +3,7 @@ * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. * Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com> - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -1447,7 +1447,8 @@ release: static void ath10k_sdio_sleep_timer_handler(struct timer_list *t) { - struct ath10k_sdio *ar_sdio = from_timer(ar_sdio, t, sleep_timer); + struct ath10k_sdio *ar_sdio = timer_container_of(ar_sdio, t, + sleep_timer); ar_sdio->mbox_state = SDIO_MBOX_REQUEST_TO_SLEEP_STATE; queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work); @@ -1621,7 +1622,7 @@ static void ath10k_sdio_hif_power_down(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n"); - del_timer_sync(&ar_sdio->sleep_timer); + timer_delete_sync(&ar_sdio->sleep_timer); ath10k_sdio_set_mbox_sleep(ar, true); /* Disable the card */ @@ -1844,7 +1845,7 @@ static int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar) ret = ath10k_sdio_diag_read32(ar, addr, &val); if (ret) { ath10k_warn(ar, - "unable to read hi_acs_flags for htt tx comple : %d\n", ret); + "unable to read hi_acs_flags for htt tx complete: %d\n", ret); return ret; } @@ -2532,7 +2533,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, return -ENOMEM; } - netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll); + netif_napi_add(ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll); ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", @@ -2648,9 +2649,9 @@ static void ath10k_sdio_remove(struct sdio_func *func) netif_napi_del(&ar->napi); - ath10k_core_destroy(ar); - destroy_workqueue(ar_sdio->workqueue); + + ath10k_core_destroy(ar); } static const struct sdio_device_id ath10k_sdio_devices[] = { @@ -2667,29 +2668,10 @@ static struct sdio_driver ath10k_sdio_driver = { .probe = ath10k_sdio_probe, .remove = ath10k_sdio_remove, .drv = { - .owner = THIS_MODULE, .pm = ATH10K_SDIO_PM_OPS, }, }; - -static int __init ath10k_sdio_init(void) -{ - int ret; - - ret = sdio_register_driver(&ath10k_sdio_driver); - if (ret) - pr_err("sdio driver registration failed: %d\n", ret); - - return ret; -} - -static void __exit ath10k_sdio_exit(void) -{ - sdio_unregister_driver(&ath10k_sdio_driver); -} - -module_init(ath10k_sdio_init); -module_exit(ath10k_sdio_exit); +module_sdio_driver(ath10k_sdio_driver); MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN SDIO devices"); diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 2c39bad7ebfb..d51f2e5a79a4 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -644,7 +644,8 @@ static void ath10k_snoc_htt_rx_cb(struct ath10k_ce_pipe *ce_state) static void ath10k_snoc_rx_replenish_retry(struct timer_list *t) { - struct ath10k_snoc *ar_snoc = from_timer(ar_snoc, t, rx_post_retry); + struct ath10k_snoc *ar_snoc = timer_container_of(ar_snoc, t, + rx_post_retry); struct ath10k *ar = ar_snoc->ar; ath10k_snoc_rx_post(ar); @@ -911,7 +912,7 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar) struct ath10k_snoc_pipe *pipe_info; int pipe_num; - del_timer_sync(&ar_snoc->rx_post_retry); + timer_delete_sync(&ar_snoc->rx_post_retry); for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { pipe_info = &ar_snoc->pipe_info[pipe_num]; ath10k_snoc_rx_pipe_cleanup(pipe_info); @@ -935,9 +936,11 @@ static int ath10k_snoc_hif_start(struct ath10k *ar) bitmap_clear(ar_snoc->pending_ce_irqs, 0, CE_COUNT_MAX); - dev_set_threaded(&ar->napi_dev, true); + dev_set_threaded(ar->napi_dev, true); ath10k_core_napi_enable(ar); - ath10k_snoc_irq_enable(ar); + /* IRQs are left enabled when we restart due to a firmware crash */ + if (!test_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags)) + ath10k_snoc_irq_enable(ar); ath10k_snoc_rx_post(ar); clear_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags); @@ -1253,7 +1256,7 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget) static void ath10k_snoc_init_napi(struct ath10k *ar) { - netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll); + netif_napi_add(ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll); } static int ath10k_snoc_request_irq(struct ath10k *ar) @@ -1338,6 +1341,9 @@ static void ath10k_snoc_quirks_init(struct ath10k *ar) struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct device *dev = &ar_snoc->dev->dev; + /* ignore errors, keep NULL if there is no property */ + of_property_read_string(dev->of_node, "firmware-name", &ar->board_name); + if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-8bit-quirk")) set_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags); } @@ -1632,10 +1638,10 @@ static int ath10k_fw_init(struct ath10k *ar) ar_snoc->fw.dev = &pdev->dev; - iommu_dom = iommu_domain_alloc(&platform_bus_type); - if (!iommu_dom) { + iommu_dom = iommu_paging_domain_alloc(ar_snoc->fw.dev); + if (IS_ERR(iommu_dom)) { ath10k_err(ar, "failed to allocate iommu domain\n"); - ret = -ENOMEM; + ret = PTR_ERR(iommu_dom); goto err_unregister; } @@ -1882,11 +1888,11 @@ static void ath10k_snoc_shutdown(struct platform_device *pdev) } static struct platform_driver ath10k_snoc_driver = { - .probe = ath10k_snoc_probe, - .remove_new = ath10k_snoc_remove, + .probe = ath10k_snoc_probe, + .remove = ath10k_snoc_remove, .shutdown = ath10k_snoc_shutdown, .driver = { - .name = "ath10k_snoc", + .name = "ath10k_snoc", .of_match_table = ath10k_snoc_dt_match, }, }; diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h index ec556bb88d65..ba37e6c7ced0 100644 --- a/drivers/net/wireless/ath/ath10k/targaddrs.h +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -491,4 +491,7 @@ struct host_interest { #define QCA4019_BOARD_DATA_SZ 12064 #define QCA4019_BOARD_EXT_DATA_SZ 0 +#define WCN3990_BOARD_DATA_SZ 26328 +#define WCN3990_BOARD_EXT_DATA_SZ 0 + #endif /* __TARGADDRS_H__ */ diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 7a9b9bbcdbfc..3fcefc55b74f 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -35,7 +35,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) int ret; ath10k_dbg(ar, ATH10K_DBG_TESTMODE, - "testmode event wmi cmd_id %d skb %pK skb->len %d\n", + "testmode event wmi cmd_id %d skb %p skb->len %d\n", cmd_id, skb, skb->len); ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len); @@ -397,7 +397,7 @@ static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[]) cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]); ath10k_dbg(ar, ATH10K_DBG_TESTMODE, - "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", + "testmode cmd wmi cmd_id %d buf %p buf_len %d\n", cmd_id, buf, buf_len); ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len); diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index 31c8d7fbb095..8b15ec07b107 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -100,7 +100,7 @@ static ssize_t ath10k_thermal_show_temp(struct device *dev, spin_unlock_bh(&ar->data_lock); /* display in millidegree celsius */ - ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); + ret = sysfs_emit(buf, "%d\n", temperature * 1000); out: mutex_unlock(&ar->conf_mutex); return ret; diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 64e7a767d963..68b78ca17eaa 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -55,8 +55,8 @@ DECLARE_EVENT_CLASS(ath10k_log_event, __vstring(msg, vaf->fmt, vaf->va) ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __assign_vstr(msg, vaf->fmt, vaf->va); ), TP_printk( @@ -92,8 +92,8 @@ TRACE_EVENT(ath10k_log_dbg, __vstring(msg, vaf->fmt, vaf->va) ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->level = level; __assign_vstr(msg, vaf->fmt, vaf->va); ), @@ -121,10 +121,10 @@ TRACE_EVENT(ath10k_log_dbg_dump, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); - __assign_str(msg, msg); - __assign_str(prefix, prefix); + __assign_str(device); + __assign_str(driver); + __assign_str(msg); + __assign_str(prefix); __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); ), @@ -152,8 +152,8 @@ TRACE_EVENT(ath10k_wmi_cmd, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->id = id; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); @@ -182,8 +182,8 @@ TRACE_EVENT(ath10k_wmi_event, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->id = id; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); @@ -211,8 +211,8 @@ TRACE_EVENT(ath10k_htt_stats, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); ), @@ -239,8 +239,8 @@ TRACE_EVENT(ath10k_wmi_dbglog, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->hw_type = ar->hw_rev; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); @@ -269,8 +269,8 @@ TRACE_EVENT(ath10k_htt_pktlog, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->hw_type = ar->hw_rev; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(pktlog), buf, buf_len); @@ -301,8 +301,8 @@ TRACE_EVENT(ath10k_htt_tx, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->msdu_id = msdu_id; __entry->msdu_len = msdu_len; __entry->vdev_id = vdev_id; @@ -332,8 +332,8 @@ TRACE_EVENT(ath10k_txrx_tx_unref, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->msdu_id = msdu_id; ), @@ -358,8 +358,8 @@ DECLARE_EVENT_CLASS(ath10k_hdr_event, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = ath10k_frm_hdr_len(data, len); memcpy(__get_dynamic_array(data), data, __entry->len); ), @@ -386,8 +386,8 @@ DECLARE_EVENT_CLASS(ath10k_payload_event, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len - ath10k_frm_hdr_len(data, len); memcpy(__get_dynamic_array(payload), data + ath10k_frm_hdr_len(data, len), __entry->len); @@ -435,8 +435,8 @@ TRACE_EVENT(ath10k_htt_rx_desc, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->hw_type = ar->hw_rev; __entry->len = len; memcpy(__get_dynamic_array(rxdesc), data, len); @@ -472,8 +472,8 @@ TRACE_EVENT(ath10k_wmi_diag_container, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->type = type; __entry->timestamp = timestamp; __entry->code = code; @@ -505,8 +505,8 @@ TRACE_EVENT(ath10k_wmi_diag, ), TP_fast_assign( - __assign_str(device, dev_name(ar->dev)); - __assign_str(driver, dev_driver_string(ar->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; memcpy(__get_dynamic_array(data), data, len); ), diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index da3bc35e41aa..493bfb410aff 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -35,7 +35,7 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) complete(&ar->offchan_tx_completed); ar->offchan_tx_skb = NULL; /* just for sanity */ - ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %pK\n", skb); + ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb); out: spin_unlock_bh(&ar->data_lock); } diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index 3c482baacec1..1732a4f98418 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -131,7 +131,7 @@ static void ath10k_usb_recv_complete(struct urb *urb) int status = 0; ath10k_dbg(ar, ATH10K_DBG_USB_BULK, - "usb recv pipe %d stat %d len %d urb 0x%pK\n", + "usb recv pipe %d stat %d len %d urb 0x%p\n", pipe->logical_pipe_num, urb->status, urb->actual_length, urb); @@ -230,7 +230,7 @@ static void ath10k_usb_post_recv_transfers(struct ath10k *ar, ath10k_usb_recv_complete, urb_context); ath10k_dbg(ar, ATH10K_DBG_USB_BULK, - "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%pK\n", + "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%p\n", recv_pipe->logical_pipe_num, recv_pipe->usb_pipe_handle, recv_pipe->ep_address, ATH10K_USB_RX_BUFFER_SIZE, urb_context->skb); @@ -1014,7 +1014,7 @@ static int ath10k_usb_probe(struct usb_interface *interface, return -ENOMEM; } - netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_usb_napi_poll); + netif_napi_add(ar->napi_dev, &ar->napi, ath10k_usb_napi_poll); usb_get_dev(dev); vendor_id = le16_to_cpu(dev->descriptor.idVendor); diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index aa57d807491c..f3f6b5954b27 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -226,7 +226,10 @@ struct wmi_ops { const struct wmi_bb_timing_cfg_arg *arg); struct sk_buff *(*gen_per_peer_per_tid_cfg)(struct ath10k *ar, const struct wmi_per_peer_per_tid_cfg_arg *arg); + struct sk_buff *(*gen_gpio_config)(struct ath10k *ar, u32 gpio_num, + u32 input, u32 pull_type, u32 intr_mode); + struct sk_buff *(*gen_gpio_output)(struct ath10k *ar, u32 gpio_num, u32 set); }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -1122,6 +1125,35 @@ ath10k_wmi_force_fw_hang(struct ath10k *ar, return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); } +static inline int ath10k_wmi_gpio_config(struct ath10k *ar, u32 gpio_num, + u32 input, u32 pull_type, u32 intr_mode) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_gpio_config) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_gpio_config(ar, gpio_num, input, pull_type, intr_mode); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->gpio_config_cmdid); +} + +static inline int ath10k_wmi_gpio_output(struct ath10k *ar, u32 gpio_num, u32 set) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_gpio_config) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_gpio_output(ar, gpio_num, set); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->gpio_output_cmdid); +} + static inline int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u64 module_enable, u32 log_level) { diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index aed97fd121ba..16d07d619b4d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3043,9 +3043,14 @@ ath10k_wmi_tlv_op_cleanup_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu) { struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu); + struct ath10k_mgmt_tx_pkt_addr *pkt_addr; struct ath10k_wmi *wmi = &ar->wmi; - idr_remove(&wmi->mgmt_pending_tx, cb->msdu_id); + spin_lock_bh(&ar->data_lock); + pkt_addr = idr_remove(&wmi->mgmt_pending_tx, cb->msdu_id); + spin_unlock_bh(&ar->data_lock); + + kfree(pkt_addr); return 0; } @@ -4606,6 +4611,8 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_echo = ath10k_wmi_tlv_op_gen_echo, .gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf, .gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable, + /* .gen_gpio_config not implemented */ + /* .gen_gpio_output not implemented */ }; static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 2e9661f4bea8..df6a24f8f8d5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1763,12 +1763,32 @@ void ath10k_wmi_put_wmi_channel(struct ath10k *ar, struct wmi_channel *ch, int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { - unsigned long time_left; + unsigned long time_left, i; time_left = wait_for_completion_timeout(&ar->wmi.service_ready, WMI_SERVICE_READY_TIMEOUT_HZ); - if (!time_left) - return -ETIMEDOUT; + if (!time_left) { + /* Sometimes the PCI HIF doesn't receive interrupt + * for the service ready message even if the buffer + * was completed. PCIe sniffer shows that it's + * because the corresponding CE ring doesn't fires + * it. Workaround here by polling CE rings once. + */ + ath10k_warn(ar, "failed to receive service ready completion, polling..\n"); + + for (i = 0; i < CE_COUNT; i++) + ath10k_hif_send_complete_check(ar, i, 1); + + time_left = wait_for_completion_timeout(&ar->wmi.service_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + if (!time_left) { + ath10k_warn(ar, "polling timed out\n"); + return -ETIMEDOUT; + } + + ath10k_warn(ar, "service ready completion received, continuing normally\n"); + } + return 0; } @@ -2009,7 +2029,7 @@ ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr)); memcpy(cmd->buf, msdu->data, msdu->len); - ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %pK len %d ftype %02x stype %02x\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", msdu, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); trace_ath10k_tx_hdr(ar, skb->data, skb->len); @@ -2421,6 +2441,7 @@ wmi_process_mgmt_tx_comp(struct ath10k *ar, struct mgmt_tx_compl_params *param) dma_unmap_single(ar->dev, pkt_addr->paddr, msdu->len, DMA_TO_DEVICE); info = IEEE80211_SKB_CB(msdu); + kfree(pkt_addr); if (param->status) { info->flags &= ~IEEE80211_TX_STAT_ACK; @@ -2616,7 +2637,7 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->boottime_ns = ktime_get_boottime_ns(); ath10k_dbg(ar, ATH10K_DBG_MGMT, - "event mgmt rx skb %pK len %d ftype %02x stype %02x\n", + "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); @@ -3970,7 +3991,7 @@ static void ath10k_radar_detected(struct ath10k *ar) if (ar->dfs_block_radar_events) ath10k_info(ar, "DFS Radar detected, but ignored as requested\n"); else - ieee80211_radar_detected(ar->hw); + ieee80211_radar_detected(ar->hw, NULL); } static void ath10k_radar_confirmation_work(struct work_struct *work) @@ -7473,6 +7494,49 @@ ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id, return skb; } +static struct sk_buff *ath10k_wmi_op_gen_gpio_config(struct ath10k *ar, + u32 gpio_num, u32 input, + u32 pull_type, u32 intr_mode) +{ + struct wmi_gpio_config_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_gpio_config_cmd *)skb->data; + cmd->pull_type = __cpu_to_le32(pull_type); + cmd->gpio_num = __cpu_to_le32(gpio_num); + cmd->input = __cpu_to_le32(input); + cmd->intr_mode = __cpu_to_le32(intr_mode); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_config gpio_num 0x%08x input 0x%08x pull_type 0x%08x intr_mode 0x%08x\n", + gpio_num, input, pull_type, intr_mode); + + return skb; +} + +static struct sk_buff *ath10k_wmi_op_gen_gpio_output(struct ath10k *ar, + u32 gpio_num, u32 set) +{ + struct wmi_gpio_output_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_gpio_output_cmd *)skb->data; + cmd->gpio_num = __cpu_to_le32(gpio_num); + cmd->set = __cpu_to_le32(set); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi gpio_output gpio_num 0x%08x set 0x%08x\n", + gpio_num, set); + + return skb; +} + static struct sk_buff * ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id, enum wmi_sta_ps_mode psmode) @@ -9137,6 +9201,9 @@ static const struct wmi_ops wmi_ops = { .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, .gen_echo = ath10k_wmi_op_gen_echo, + .gen_gpio_config = ath10k_wmi_op_gen_gpio_config, + .gen_gpio_output = ath10k_wmi_op_gen_gpio_output, + /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -9207,6 +9274,8 @@ static const struct wmi_ops wmi_10_1_ops = { .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, .gen_echo = ath10k_wmi_op_gen_echo, + .gen_gpio_config = ath10k_wmi_op_gen_gpio_config, + .gen_gpio_output = ath10k_wmi_op_gen_gpio_output, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -9279,6 +9348,8 @@ static const struct wmi_ops wmi_10_2_ops = { .gen_delba_send = ath10k_wmi_op_gen_delba_send, .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, + .gen_gpio_config = ath10k_wmi_op_gen_gpio_config, + .gen_gpio_output = ath10k_wmi_op_gen_gpio_output, /* .gen_pdev_enable_adaptive_cca not implemented */ }; @@ -9350,6 +9421,8 @@ static const struct wmi_ops wmi_10_2_4_ops = { ath10k_wmi_op_gen_pdev_enable_adaptive_cca, .get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype, .gen_bb_timing = ath10k_wmi_10_2_4_op_gen_bb_timing, + .gen_gpio_config = ath10k_wmi_op_gen_gpio_config, + .gen_gpio_output = ath10k_wmi_op_gen_gpio_output, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -9431,6 +9504,8 @@ static const struct wmi_ops wmi_10_4_ops = { .gen_pdev_bss_chan_info_req = ath10k_wmi_10_2_op_gen_pdev_bss_chan_info, .gen_echo = ath10k_wmi_op_gen_echo, .gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config, + .gen_gpio_config = ath10k_wmi_op_gen_gpio_config, + .gen_gpio_output = ath10k_wmi_op_gen_gpio_output, }; int ath10k_wmi_attach(struct ath10k *ar) @@ -9538,6 +9613,7 @@ static int ath10k_wmi_mgmt_tx_clean_up_pending(int msdu_id, void *ptr, dma_unmap_single(ar->dev, pkt_addr->paddr, msdu->len, DMA_TO_DEVICE); ieee80211_free_txskb(ar->hw, msdu); + kfree(pkt_addr); return 0; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 2379501225a4..0faefc0a9a40 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3034,6 +3034,41 @@ enum wmi_10_4_feature_mask { }; +/* WMI_GPIO_CONFIG_CMDID */ +enum { + WMI_GPIO_PULL_NONE, + WMI_GPIO_PULL_UP, + WMI_GPIO_PULL_DOWN, +}; + +enum { + WMI_GPIO_INTTYPE_DISABLE, + WMI_GPIO_INTTYPE_RISING_EDGE, + WMI_GPIO_INTTYPE_FALLING_EDGE, + WMI_GPIO_INTTYPE_BOTH_EDGE, + WMI_GPIO_INTTYPE_LEVEL_LOW, + WMI_GPIO_INTTYPE_LEVEL_HIGH +}; + +/* WMI_GPIO_CONFIG_CMDID */ +struct wmi_gpio_config_cmd { + __le32 gpio_num; /* GPIO number to be setup */ + __le32 input; /* 0 - Output/ 1 - Input */ + __le32 pull_type; /* Pull type defined above */ + __le32 intr_mode; /* Interrupt mode defined above (Input) */ +} __packed; + +/* WMI_GPIO_OUTPUT_CMDID */ +struct wmi_gpio_output_cmd { + __le32 gpio_num; /* GPIO number to be setup */ + __le32 set; /* Set the GPIO pin*/ +} __packed; + +/* WMI_GPIO_INPUT_EVENTID */ +struct wmi_gpio_input_event { + __le32 gpio_num; /* GPIO number which changed state */ +} __packed; + struct wmi_ext_resource_config_10_4_cmd { /* contains enum wmi_host_platform_type */ __le32 host_platform_config; diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig index 27f0523bf967..659ef134ef16 100644 --- a/drivers/net/wireless/ath/ath11k/Kconfig +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -24,6 +24,7 @@ config ATH11K_PCI select MHI_BUS select QRTR select QRTR_MHI + select PCI_PWRCTRL_PWRSEQ if HAVE_PWRCTRL help This module adds support for PCIE bus diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile index 2c94d50ae36f..d9092414b362 100644 --- a/drivers/net/wireless/ath/ath11k/Makefile +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -18,7 +18,8 @@ ath11k-y += core.o \ dbring.o \ hw.o \ pcic.o \ - fw.o + fw.o \ + p2p.o ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o @@ -26,6 +27,7 @@ ath11k-$(CONFIG_ATH11K_TRACING) += trace.o ath11k-$(CONFIG_THERMAL) += thermal.o ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o ath11k-$(CONFIG_PM) += wow.o +ath11k-$(CONFIG_DEV_COREDUMP) += coredump.o obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o ath11k_ahb-y += ahb.o diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 7c0a23517949..fde1ce43c499 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -397,7 +397,7 @@ static void ath11k_ahb_stop(struct ath11k_base *ab) ath11k_ahb_ce_irqs_disable(ab); ath11k_ahb_sync_ce_irqs(ab); ath11k_ahb_kill_tasklets(ab); - del_timer_sync(&ab->rx_replenish_retry); + timer_delete_sync(&ab->rx_replenish_retry); ath11k_ce_cleanup_pipes(ab); } @@ -413,7 +413,7 @@ static int ath11k_ahb_power_up(struct ath11k_base *ab) return ret; } -static void ath11k_ahb_power_down(struct ath11k_base *ab) +static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend) { struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); @@ -442,6 +442,7 @@ static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); netif_napi_del(&irq_grp->napi); + free_netdev(irq_grp->napi_ndev); } } @@ -533,8 +534,12 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) irq_grp->ab = ab; irq_grp->grp_id = i; - init_dummy_netdev(&irq_grp->napi_ndev); - netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + + irq_grp->napi_ndev = alloc_netdev_dummy(0); + if (!irq_grp->napi_ndev) + return -ENOMEM; + + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath11k_ahb_ext_grp_napi_poll); for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { @@ -949,6 +954,36 @@ static int ath11k_ahb_setup_msa_resources(struct ath11k_base *ab) return 0; } +static int ath11k_ahb_ce_remap(struct ath11k_base *ab) +{ + const struct ce_remap *ce_remap = ab->hw_params.ce_remap; + struct platform_device *pdev = ab->pdev; + + if (!ce_remap) { + /* no separate CE register space */ + ab->mem_ce = ab->mem; + return 0; + } + + /* ce register space is moved out of wcss unlike ipq8074 or ipq6018 + * and the space is not contiguous, hence remapping the CE registers + * to a new space for accessing them. + */ + ab->mem_ce = ioremap(ce_remap->base, ce_remap->size); + if (!ab->mem_ce) { + dev_err(&pdev->dev, "ce ioremap error\n"); + return -ENOMEM; + } + + return 0; +} + +static void ath11k_ahb_ce_unmap(struct ath11k_base *ab) +{ + if (ab->hw_params.ce_remap) + iounmap(ab->mem_ce); +} + static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) { struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); @@ -965,18 +1000,18 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) if (!ab->hw_params.fixed_fw_mem) return 0; - ret = ath11k_ahb_setup_msa_resources(ab); - if (ret) { - ath11k_err(ab, "failed to setup msa resources\n"); - return ret; - } - node = of_get_child_by_name(host_dev->of_node, "wifi-firmware"); if (!node) { ab_ahb->fw.use_tz = true; return 0; } + ret = ath11k_ahb_setup_msa_resources(ab); + if (ret) { + ath11k_err(ab, "failed to setup msa resources\n"); + return ret; + } + info.fwnode = &node->fwnode; info.parent = host_dev; info.name = node->name; @@ -996,10 +1031,10 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) ab_ahb->fw.dev = &pdev->dev; - iommu_dom = iommu_domain_alloc(&platform_bus_type); - if (!iommu_dom) { + iommu_dom = iommu_paging_domain_alloc(ab_ahb->fw.dev); + if (IS_ERR(iommu_dom)) { ath11k_err(ab, "failed to allocate iommu domain\n"); - ret = -ENOMEM; + ret = PTR_ERR(iommu_dom); goto err_unregister; } @@ -1141,25 +1176,13 @@ static int ath11k_ahb_probe(struct platform_device *pdev) if (ret) goto err_core_free; - ab->mem_ce = ab->mem; - - if (ab->hw_params.ce_remap) { - const struct ce_remap *ce_remap = ab->hw_params.ce_remap; - /* ce register space is moved out of wcss unlike ipq8074 or ipq6018 - * and the space is not contiguous, hence remapping the CE registers - * to a new space for accessing them. - */ - ab->mem_ce = ioremap(ce_remap->base, ce_remap->size); - if (!ab->mem_ce) { - dev_err(&pdev->dev, "ce ioremap error\n"); - ret = -ENOMEM; - goto err_core_free; - } - } + ret = ath11k_ahb_ce_remap(ab); + if (ret) + goto err_core_free; ret = ath11k_ahb_fw_resources_init(ab); if (ret) - goto err_core_free; + goto err_ce_unmap; ret = ath11k_ahb_setup_smp2p_handle(ab); if (ret) @@ -1211,6 +1234,9 @@ err_release_smp2p_handle: err_fw_deinit: ath11k_ahb_fw_resource_deinit(ab); +err_ce_unmap: + ath11k_ahb_ce_unmap(ab); + err_core_free: ath11k_core_free(ab); platform_set_drvdata(pdev, NULL); @@ -1243,9 +1269,7 @@ static void ath11k_ahb_free_resources(struct ath11k_base *ab) ath11k_ahb_release_smp2p_handle(ab); ath11k_ahb_fw_resource_deinit(ab); ath11k_ce_free_pipes(ab); - - if (ab->hw_params.ce_remap) - iounmap(ab->mem_ce); + ath11k_ahb_ce_unmap(ab); ath11k_core_free(ab); platform_set_drvdata(pdev, NULL); @@ -1256,7 +1280,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev) struct ath11k_base *ab = platform_get_drvdata(pdev); if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { - ath11k_ahb_power_down(ab); + ath11k_ahb_power_down(ab, false); ath11k_debugfs_soc_destroy(ab); ath11k_qmi_deinit_service(ab); goto qmi_fail; @@ -1266,6 +1290,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev) ath11k_core_deinit(ab); qmi_fail: + ath11k_fw_destroy(ab); ath11k_ahb_free_resources(ab); } @@ -1285,16 +1310,17 @@ static void ath11k_ahb_shutdown(struct platform_device *pdev) ath11k_core_deinit(ab); free_resources: + ath11k_fw_destroy(ab); ath11k_ahb_free_resources(ab); } static struct platform_driver ath11k_ahb_driver = { - .driver = { - .name = "ath11k", + .driver = { + .name = "ath11k", .of_match_table = ath11k_ahb_of_match, }, - .probe = ath11k_ahb_probe, - .remove_new = ath11k_ahb_remove, + .probe = ath11k_ahb_probe, + .remove = ath11k_ahb_remove, .shutdown = ath11k_ahb_shutdown, }; diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index e66e86bdec20..746038006eb4 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -393,11 +393,10 @@ static int ath11k_ce_completed_recv_next(struct ath11k_ce_pipe *pipe, goto err; } + /* Make sure descriptor is read after the head pointer. */ + dma_rmb(); + *nbytes = ath11k_hal_ce_dst_status_get_length(desc); - if (*nbytes == 0) { - ret = -EIO; - goto err; - } *skb = pipe->dest_ring->skb[sw_index]; pipe->dest_ring->skb[sw_index] = NULL; @@ -430,8 +429,8 @@ static void ath11k_ce_recv_process_cb(struct ath11k_ce_pipe *pipe) dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); - if (unlikely(max_nbytes < nbytes)) { - ath11k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + if (unlikely(max_nbytes < nbytes || nbytes == 0)) { + ath11k_warn(ab, "unexpected rx length (nbytes %d, max %d)", nbytes, max_nbytes); dev_kfree_skb_any(skb); continue; @@ -908,7 +907,7 @@ EXPORT_SYMBOL(ath11k_ce_rx_post_buf); void ath11k_ce_rx_replenish_retry(struct timer_list *t) { - struct ath11k_base *ab = from_timer(ab, t, rx_replenish_retry); + struct ath11k_base *ab = timer_container_of(ab, t, rx_replenish_retry); ath11k_ce_rx_post_buf(ab); } diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index 69946fc70077..bcde2fcf02cf 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_CE_H @@ -146,7 +146,7 @@ struct ath11k_ce_ring { /* Host address space */ void *base_addr_owner_space_unaligned; /* CE address space */ - u32 base_addr_ce_space_unaligned; + dma_addr_t base_addr_ce_space_unaligned; /* Actual start of descriptors. * Aligned to descriptor-size boundary. @@ -156,7 +156,7 @@ struct ath11k_ce_ring { void *base_addr_owner_space; /* CE address space */ - u32 base_addr_ce_space; + dma_addr_t base_addr_ce_space; /* HAL ring id */ u32 hal_ring_id; diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index c78bce19bd75..22a101136135 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.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-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -62,7 +62,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .single_pdev_only = false, .rxdma1_enable = true, - .num_rxmda_per_pdev = 1, + .num_rxdma_per_pdev = 1, .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, @@ -123,6 +123,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, .support_dual_stations = false, + .pdev_suspend = false, }, { .hw_rev = ATH11K_HW_IPQ6018_HW10, @@ -148,7 +149,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .single_pdev_only = false, .rxdma1_enable = true, - .num_rxmda_per_pdev = 1, + .num_rxdma_per_pdev = 1, .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, @@ -207,6 +208,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = false, .support_fw_mac_sequence = false, .support_dual_stations = false, + .pdev_suspend = false, }, { .name = "qca6390 hw2.0", @@ -232,7 +234,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .single_pdev_only = true, .rxdma1_enable = false, - .num_rxmda_per_pdev = 2, + .num_rxdma_per_pdev = 2, .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, @@ -247,7 +249,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { }, .interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP), + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), .supports_monitor = false, .full_monitor_mode = false, .supports_shadow_regs = true, @@ -293,6 +298,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = false, .support_fw_mac_sequence = true, .support_dual_stations = true, + .pdev_suspend = false, }, { .name = "qcn9074 hw1.0", @@ -317,7 +323,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .svc_to_ce_map_len = 18, .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .rxdma1_enable = true, - .num_rxmda_per_pdev = 1, + .num_rxdma_per_pdev = 1, .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, @@ -376,6 +382,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = false, .support_fw_mac_sequence = false, .support_dual_stations = false, + .pdev_suspend = false, }, { .name = "wcn6855 hw2.0", @@ -401,7 +408,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .single_pdev_only = true, .rxdma1_enable = false, - .num_rxmda_per_pdev = 2, + .num_rxdma_per_pdev = 2, .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, @@ -416,7 +423,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { }, .interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP), + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), .supports_monitor = false, .full_monitor_mode = false, .supports_shadow_regs = true, @@ -462,6 +472,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = false, .support_fw_mac_sequence = true, .support_dual_stations = true, + .pdev_suspend = false, }, { .name = "wcn6855 hw2.1", @@ -486,7 +497,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .svc_to_ce_map_len = 14, .single_pdev_only = true, .rxdma1_enable = false, - .num_rxmda_per_pdev = 2, + .num_rxdma_per_pdev = 2, .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, @@ -501,7 +512,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { }, .interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP), + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), .supports_monitor = false, .supports_shadow_regs = true, .idle_ps = true, @@ -546,6 +560,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = false, .support_fw_mac_sequence = true, .support_dual_stations = true, + .pdev_suspend = false, }, { .name = "wcn6750 hw1.0", @@ -571,7 +586,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .single_pdev_only = true, .rxdma1_enable = false, - .num_rxmda_per_pdev = 1, + .num_rxdma_per_pdev = 1, .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, @@ -595,7 +610,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = true, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 16 + 1, + .num_vdevs = 3, .num_peers = 512, .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), @@ -607,7 +622,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = false, .alloc_cacheable_memory = false, .supports_rssi_stats = true, - .fw_wmi_diag_event = false, + .fw_wmi_diag_event = true, .current_cc_support = true, .dbr_debug_support = false, .global_reset = false, @@ -628,6 +643,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = true, .support_fw_mac_sequence = true, .support_dual_stations = false, + .pdev_suspend = true, }, { .hw_rev = ATH11K_HW_IPQ5018_HW10, @@ -664,7 +680,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq5018, .ce_remap = &ath11k_ce_remap_ipq5018, .rxdma1_enable = true, - .num_rxmda_per_pdev = RXDMA_PER_PDEV_5018, + .num_rxdma_per_pdev = RXDMA_PER_PDEV_5018, .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, @@ -710,6 +726,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .smp2p_wow_exit = false, .support_fw_mac_sequence = false, .support_dual_stations = false, + .pdev_suspend = false, }, { .name = "qca2066 hw2.1", @@ -735,7 +752,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, .single_pdev_only = true, .rxdma1_enable = false, - .num_rxmda_per_pdev = 2, + .num_rxdma_per_pdev = 2, .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, @@ -750,7 +767,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { }, .interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP), + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), .supports_monitor = false, .full_monitor_mode = false, .supports_shadow_regs = true, @@ -797,14 +817,141 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .support_fw_mac_sequence = true, .support_dual_stations = true, }, -}; + { + .name = "qca6698aq hw2.1", + .hw_rev = ATH11K_HW_QCA6698AQ_HW21, + .fw = { + .dir = "QCA6698AQ/hw2.1", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &wcn6855_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &wcn6855_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, + .target_ce_config = ath11k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxdma_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, -static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) -{ - WARN_ON(!ab->hw_params.single_pdev_only); + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + .fragment_160mhz = false, + }, - return &ab->pdevs[0]; -} + .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, + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .coldboot_cal_mm = false, + .coldboot_cal_ftm = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 2 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, + .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, + .support_off_channel_tx = true, + .supports_multi_bssid = true, + + .sram_dump = { + .start = 0x01400000, + .end = 0x0177ffff, + }, + + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, + .support_dual_stations = true, + .pdev_suspend = false, + }, +}; + +static const struct dmi_system_id ath11k_pm_quirk_table[] = { + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21J4"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21K4"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21K6"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21K8"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21KA"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21F9"), + }, + }, + {} +}; void ath11k_fw_stats_pdevs_free(struct list_head *head) { @@ -843,6 +990,7 @@ void ath11k_fw_stats_init(struct ath11k *ar) INIT_LIST_HEAD(&ar->fw_stats.bcn); init_completion(&ar->fw_stats_complete); + init_completion(&ar->fw_stats_done); } void ath11k_fw_stats_free(struct ath11k_fw_stats *stats) @@ -864,23 +1012,33 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab) return ab->hw_params.coldboot_cal_mm; } -int ath11k_core_suspend(struct ath11k_base *ab) +/* Check if we need to continue with suspend/resume operation. + * Return: + * a negative value: error happens and don't continue. + * 0: no error but don't continue. + * positive value: no error and do continue. + */ +static int ath11k_core_continue_suspend_resume(struct ath11k_base *ab) { - int ret; - struct ath11k_pdev *pdev; struct ath11k *ar; if (!ab->hw_params.supports_suspend) return -EOPNOTSUPP; /* so far single_pdev_only chips have supports_suspend as true - * and only the first pdev is valid. + * so pass 0 as a dummy pdev_id here. */ - pdev = ath11k_core_get_single_pdev(ab); - ar = pdev->ar; + ar = ab->pdevs[0].ar; if (!ar || ar->state != ATH11K_STATE_OFF) return 0; + return 1; +} + +static int ath11k_core_suspend_wow(struct ath11k_base *ab) +{ + int ret; + ret = ath11k_dp_rx_pktlog_stop(ab, true); if (ret) { ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n", @@ -888,7 +1046,10 @@ int ath11k_core_suspend(struct ath11k_base *ab) return ret; } - ret = ath11k_mac_wait_tx_complete(ar); + /* So far only single_pdev_only devices can reach here, + * so it is valid to handle the first, and the only, pdev. + */ + ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar); if (ret) { ath11k_warn(ab, "failed to wait tx complete: %d\n", ret); return ret; @@ -921,24 +1082,146 @@ int ath11k_core_suspend(struct ath11k_base *ab) return 0; } + +static int ath11k_core_suspend_default(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_dp_rx_pktlog_stop(ab, true); + if (ret) { + ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n", + ret); + return ret; + } + + /* So far only single_pdev_only devices can reach here, + * so it is valid to handle the first, and the only, pdev. + */ + ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar); + if (ret) { + ath11k_warn(ab, "failed to wait tx complete: %d\n", ret); + return ret; + } + + ret = ath11k_dp_rx_pktlog_stop(ab, false); + if (ret) { + ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n", + ret); + return ret; + } + + ath11k_ce_stop_shadow_timers(ab); + ath11k_dp_stop_shadow_timers(ab); + + /* PM framework skips suspend_late/resume_early callbacks + * if other devices report errors in their suspend callbacks. + * However ath11k_core_resume() would still be called because + * here we return success thus kernel put us on dpm_suspended_list. + * Since we won't go through a power down/up cycle, there is + * no chance to call complete(&ab->restart_completed) in + * ath11k_core_restart(), making ath11k_core_resume() timeout. + * So call it here to avoid this issue. This also works in case + * no error happens thus suspend_late/resume_early get called, + * because it will be reinitialized in ath11k_core_resume_early(). + */ + complete(&ab->restart_completed); + + return 0; +} + +int ath11k_core_suspend(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return ath11k_core_suspend_wow(ab); + + return ath11k_core_suspend_default(ab); +} EXPORT_SYMBOL(ath11k_core_suspend); -int ath11k_core_resume(struct ath11k_base *ab) +int ath11k_core_suspend_late(struct ath11k_base *ab) { int ret; - struct ath11k_pdev *pdev; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return 0; + + ath11k_hif_irq_disable(ab); + ath11k_hif_ce_irq_disable(ab); + + ath11k_hif_power_down(ab, true); + + return 0; +} +EXPORT_SYMBOL(ath11k_core_suspend_late); + +int ath11k_core_resume_early(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return 0; + + reinit_completion(&ab->restart_completed); + ret = ath11k_hif_power_up(ab); + if (ret) + ath11k_warn(ab, "failed to power up hif during resume: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath11k_core_resume_early); + +static int ath11k_core_resume_default(struct ath11k_base *ab) +{ struct ath11k *ar; + long time_left; + int ret; - if (!ab->hw_params.supports_suspend) - return -EOPNOTSUPP; + time_left = wait_for_completion_timeout(&ab->restart_completed, + ATH11K_RESET_TIMEOUT_HZ); + if (time_left == 0) { + ath11k_warn(ab, "timeout while waiting for restart complete"); + return -ETIMEDOUT; + } - /* so far signle_pdev_only chips have supports_suspend as true - * and only the first pdev is valid. + /* So far only single_pdev_only devices can reach here, + * so it is valid to handle the first, and the only, pdev. */ - pdev = ath11k_core_get_single_pdev(ab); - ar = pdev->ar; - if (!ar || ar->state != ATH11K_STATE_OFF) - return 0; + ar = ab->pdevs[0].ar; + if (ab->hw_params.current_cc_support && + ar->alpha2[0] != 0 && ar->alpha2[1] != 0) { + ret = ath11k_reg_set_cc(ar); + if (ret) { + ath11k_warn(ab, "failed to set country code during resume: %d\n", + ret); + return ret; + } + } + + ret = ath11k_dp_rx_pktlog_start(ab); + if (ret) + ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n", + ret); + + return ret; +} + +static int ath11k_core_resume_wow(struct ath11k_base *ab) +{ + int ret; ret = ath11k_hif_resume(ab); if (ret) { @@ -964,6 +1247,20 @@ int ath11k_core_resume(struct ath11k_base *ab) return 0; } + +int ath11k_core_resume(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return ath11k_core_resume_wow(ab); + + return ath11k_core_resume_default(ab); +} EXPORT_SYMBOL(ath11k_core_resume); static void ath11k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data) @@ -1067,9 +1364,12 @@ int ath11k_core_check_dt(struct ath11k_base *ab) if (!node) return -ENOENT; - of_property_read_string(node, "qcom,ath11k-calibration-variant", + of_property_read_string(node, "qcom,calibration-variant", &variant); if (!variant) + of_property_read_string(node, "qcom,ath11k-calibration-variant", + &variant); + if (!variant) return -ENODATA; if (strscpy(ab->qmi.target.bdf_ext, variant, max_len) < 0) @@ -1657,11 +1957,47 @@ err_pdev_debug: return ret; } +static void ath11k_core_pdev_suspend_target(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + unsigned long time_left; + int ret; + int i; + + if (!ab->hw_params.pdev_suspend) + return; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + reinit_completion(&ab->htc_suspend); + + ret = ath11k_wmi_pdev_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR, + pdev->pdev_id); + if (ret) { + ath11k_warn(ab, "could not suspend target :%d\n", ret); + /* pointless to try other pdevs */ + return; + } + + time_left = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ); + + if (!time_left) { + ath11k_warn(ab, "suspend timed out - target pause event never came\n"); + /* pointless to try other pdevs */ + return; + } + } +} + static void ath11k_core_pdev_destroy(struct ath11k_base *ab) { ath11k_spectral_deinit(ab); ath11k_thermal_unregister(ab); ath11k_mac_unregister(ab); + ath11k_core_pdev_suspend_target(ab); ath11k_hif_irq_disable(ab); ath11k_dp_pdev_free(ab); ath11k_debugfs_pdev_destroy(ab); @@ -1749,7 +2085,7 @@ static int ath11k_core_start(struct ath11k_base *ab) } /* put hardware to DBS mode */ - if (ab->hw_params.single_pdev_only && ab->hw_params.num_rxmda_per_pdev > 1) { + if (ab->hw_params.single_pdev_only && ab->hw_params.num_rxdma_per_pdev > 1) { ret = ath11k_wmi_set_hw_mode(ab, WMI_HOST_HW_MODE_DBS); if (ret) { ath11k_err(ab, "failed to send dbs mode: %d\n", ret); @@ -1799,6 +2135,20 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) { int ret; + switch (ath11k_crypto_mode) { + case ATH11K_CRYPT_MODE_SW: + set_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags); + set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); + break; + case ATH11K_CRYPT_MODE_HW: + clear_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags); + clear_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); + break; + default: + ath11k_info(ab, "invalid crypto_mode: %d\n", ath11k_crypto_mode); + return -EINVAL; + } + ret = ath11k_core_start_firmware(ab, ab->fw_mode); if (ret) { ath11k_err(ab, "failed to start firmware: %d\n", ret); @@ -1817,20 +2167,6 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) goto err_firmware_stop; } - switch (ath11k_crypto_mode) { - case ATH11K_CRYPT_MODE_SW: - set_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags); - set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); - break; - case ATH11K_CRYPT_MODE_HW: - clear_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags); - clear_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); - break; - default: - ath11k_info(ab, "invalid crypto_mode: %d\n", ath11k_crypto_mode); - return -EINVAL; - } - if (ath11k_frame_mode == ATH11K_HW_TXRX_RAW) set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); @@ -1903,6 +2239,7 @@ err_hal_srng_deinit: void ath11k_core_halt(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; + struct list_head *pos, *n; lockdep_assert_held(&ar->conf_mutex); @@ -1912,12 +2249,18 @@ void ath11k_core_halt(struct ath11k *ar) ath11k_mac_scan_finish(ar); ath11k_mac_peer_cleanup_all(ar); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->update_11d_work); rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); synchronize_rcu(); - INIT_LIST_HEAD(&ar->arvifs); + + spin_lock_bh(&ar->data_lock); + list_for_each_safe(pos, n, &ar->arvifs) + list_del_init(pos); + spin_unlock_bh(&ar->data_lock); + idr_init(&ar->txmgmt_idr); } @@ -1926,23 +2269,20 @@ static void ath11k_update_11d(struct work_struct *work) struct ath11k_base *ab = container_of(work, struct ath11k_base, update_11d_work); struct ath11k *ar; struct ath11k_pdev *pdev; - struct wmi_set_current_country_params set_current_param = {}; int ret, i; - spin_lock_bh(&ab->base_lock); - memcpy(&set_current_param.alpha2, &ab->new_alpha2, 2); - spin_unlock_bh(&ab->base_lock); - - ath11k_dbg(ab, ATH11K_DBG_WMI, "update 11d new cc %c%c\n", - set_current_param.alpha2[0], - set_current_param.alpha2[1]); - for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; - memcpy(&ar->alpha2, &set_current_param.alpha2, 2); - ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + spin_lock_bh(&ab->base_lock); + memcpy(&ar->alpha2, &ab->new_alpha2, 2); + spin_unlock_bh(&ab->base_lock); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "update 11d new cc %c%c for pdev %d\n", + ar->alpha2[0], ar->alpha2[1], i); + + ret = ath11k_reg_set_cc(ar); if (ret) ath11k_warn(ar->ab, "pdev id %d failed set current country code: %d\n", @@ -2060,6 +2400,8 @@ static void ath11k_core_restart(struct work_struct *work) if (!ab->is_reset) ath11k_core_post_reconfigure_recovery(ab); + + complete(&ab->restart_completed); } static void ath11k_core_reset(struct work_struct *work) @@ -2116,6 +2458,7 @@ static void ath11k_core_reset(struct work_struct *work) reinit_completion(&ab->recovery_start); atomic_set(&ab->recovery_start_count, 0); + ath11k_coredump_collect(ab); ath11k_core_pre_reconfigure_recovery(ab); reinit_completion(&ab->reconfigure_complete); @@ -2129,7 +2472,7 @@ static void ath11k_core_reset(struct work_struct *work) ath11k_hif_irq_disable(ab); ath11k_hif_ce_irq_disable(ab); - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n"); @@ -2179,10 +2522,62 @@ int ath11k_core_pre_init(struct ath11k_base *ab) } EXPORT_SYMBOL(ath11k_core_pre_init); +static int ath11k_core_pm_notify(struct notifier_block *nb, + unsigned long action, void *nouse) +{ + struct ath11k_base *ab = container_of(nb, struct ath11k_base, + pm_nb); + + switch (action) { + case PM_SUSPEND_PREPARE: + ab->actual_pm_policy = ab->pm_policy; + break; + case PM_HIBERNATION_PREPARE: + ab->actual_pm_policy = ATH11K_PM_DEFAULT; + break; + default: + break; + } + + return NOTIFY_OK; +} + +static int ath11k_core_pm_notifier_register(struct ath11k_base *ab) +{ + ab->pm_nb.notifier_call = ath11k_core_pm_notify; + return register_pm_notifier(&ab->pm_nb); +} + +void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab) +{ + int ret; + + ret = unregister_pm_notifier(&ab->pm_nb); + if (ret) + /* just warn here, there is nothing can be done in fail case */ + ath11k_warn(ab, "failed to unregister PM notifier %d\n", ret); +} +EXPORT_SYMBOL(ath11k_core_pm_notifier_unregister); + int ath11k_core_init(struct ath11k_base *ab) { + const struct dmi_system_id *dmi_id; int ret; + dmi_id = dmi_first_match(ath11k_pm_quirk_table); + if (dmi_id) + ab->pm_policy = (kernel_ulong_t)dmi_id->driver_data; + else + ab->pm_policy = ATH11K_PM_DEFAULT; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "pm policy %u\n", ab->pm_policy); + + ret = ath11k_core_pm_notifier_register(ab); + if (ret) { + ath11k_err(ab, "failed to register PM notifier: %d\n", ret); + return ret; + } + ret = ath11k_core_soc_create(ab); if (ret) { ath11k_err(ab, "failed to create soc core: %d\n", ret); @@ -2202,10 +2597,10 @@ void ath11k_core_deinit(struct ath11k_base *ab) mutex_unlock(&ab->core_lock); - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_mac_destroy(ab); ath11k_core_soc_destroy(ab); - ath11k_fw_destroy(ab); + ath11k_core_pm_notifier_unregister(ab); } EXPORT_SYMBOL(ath11k_core_deinit); @@ -2252,9 +2647,11 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, INIT_WORK(&ab->restart_work, ath11k_core_restart); INIT_WORK(&ab->update_11d_work, ath11k_update_11d); INIT_WORK(&ab->reset_work, ath11k_core_reset); + INIT_WORK(&ab->dump_work, ath11k_coredump_upload); timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->wow.wakeup_completed); + init_completion(&ab->restart_completed); ab->dev = dev; ab->hif.bus = bus; diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index b3fb74a226fb..6b2f207975e3 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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_CORE_H @@ -16,6 +16,7 @@ #include <linux/rhashtable.h> #include <linux/average.h> #include <linux/firmware.h> +#include <linux/suspend.h> #include "qmi.h" #include "htc.h" @@ -32,6 +33,7 @@ #include "spectral.h" #include "wow.h" #include "fw.h" +#include "coredump.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -148,6 +150,7 @@ enum ath11k_hw_rev { ATH11K_HW_WCN6750_HW10, ATH11K_HW_IPQ5018_HW10, ATH11K_HW_QCA2066_HW21, + ATH11K_HW_QCA6698AQ_HW21, }; enum ath11k_firmware_mode { @@ -174,7 +177,7 @@ struct ath11k_ext_irq_grp { u64 timestamp; bool napi_enabled; struct napi_struct napi; - struct net_device napi_ndev; + struct net_device *napi_ndev; }; enum ath11k_smbios_cc_type { @@ -330,6 +333,9 @@ struct ath11k_chan_power_info { s8 tx_power; }; +/* ath11k only deals with 160 MHz, so 8 subchannels */ +#define ATH11K_NUM_PWR_LEVELS 8 + /** * struct ath11k_reg_tpc_power_info - regulatory TPC power info * @is_psd_power: is PSD power or not @@ -337,7 +343,6 @@ struct ath11k_chan_power_info { * @ap_power_type: type of power (SP/LPI/VLP) * @num_pwr_levels: number of power levels * @reg_max: Array of maximum TX power (dBm) per PSD value - * @ap_constraint_power: AP constraint power (dBm) * @tpe: TPE values processed from TPE IE * @chan_power_info: power info to send to firmware */ @@ -346,10 +351,9 @@ struct ath11k_reg_tpc_power_info { u8 eirp_power; enum wmi_reg_6ghz_ap_type ap_power_type; u8 num_pwr_levels; - u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL]; - u8 ap_constraint_power; - s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL]; - struct ath11k_chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL]; + u8 reg_max[ATH11K_NUM_PWR_LEVELS]; + s8 tpe[ATH11K_NUM_PWR_LEVELS]; + struct ath11k_chan_power_info chan_power_info[ATH11K_NUM_PWR_LEVELS]; }; struct ath11k_vif { @@ -367,8 +371,8 @@ struct ath11k_vif { struct ath11k *ar; struct ieee80211_vif *vif; - u16 tx_seq_no; struct wmi_wmm_params_all_arg wmm_params; + struct wmi_wmm_params_all_arg muedca_params; struct list_head list; union { struct { @@ -396,6 +400,7 @@ struct ath11k_vif { u8 bssid[ETH_ALEN]; struct cfg80211_bitrate_mask bitrate_mask; struct delayed_work connection_loss_work; + struct work_struct bcn_tx_work; int num_legacy_stations; int rtscts_prot_mode; int txpower; @@ -403,11 +408,17 @@ struct ath11k_vif { bool wpaie_present; bool bcca_zero_sent; bool do_not_send_tmpl; - struct ieee80211_chanctx_conf chanctx; struct ath11k_arp_ns_offload arp_ns_offload; struct ath11k_rekey_data rekey_data; struct ath11k_reg_tpc_power_info reg_tpc_info; + + /* Must be last - ends in a flexible-array member. + * + * FIXME: Driver should not copy struct ieee80211_chanctx_conf, + * especially because it has a flexible array. Find a better way. + */ + struct ieee80211_chanctx_conf chanctx; }; struct ath11k_vif_iter { @@ -589,6 +600,8 @@ struct ath11k_fw_stats { struct list_head pdevs; struct list_head vdevs; struct list_head bcn; + u32 num_vdev_recvd; + u32 num_bcn_recvd; }; struct ath11k_dbg_htt_stats { @@ -677,7 +690,7 @@ struct ath11k { struct mutex conf_mutex; /* protects the radio specific data like debug stats, ppdu_stats_info stats, * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, - * channel context data, survey info, test mode data. + * channel context data, survey info, test mode data, channel_update_queue. */ spinlock_t data_lock; @@ -735,6 +748,9 @@ struct ath11k { struct completion bss_survey_done; struct work_struct regd_update_work; + struct work_struct channel_update_work; + /* protected with data_lock */ + struct list_head channel_update_queue; struct work_struct wmi_mgmt_tx_work; struct sk_buff_head wmi_mgmt_tx_queue; @@ -770,7 +786,7 @@ struct ath11k { u8 alpha2[REG_ALPHA2_LEN + 1]; struct ath11k_fw_stats fw_stats; struct completion fw_stats_complete; - bool fw_stats_done; + struct completion fw_stats_done; /* protected by conf_mutex */ bool ps_state_enable; @@ -879,6 +895,11 @@ struct ath11k_msi_config { u16 hw_rev; }; +enum ath11k_pm_policy { + ATH11K_PM_DEFAULT, + ATH11K_PM_WOW, +}; + /* Master structure to hold the hw data which may be used in core module */ struct ath11k_base { enum ath11k_hw_rev hw_rev; @@ -892,6 +913,10 @@ struct ath11k_base { /* HW channel counters frequency value in hertz common to all MACs */ u32 cc_freq_hz; + struct ath11k_dump_file_data *dump_data; + size_t ath11k_coredump_len; + struct work_struct dump_work; + struct ath11k_htc htc; struct ath11k_dp dp; @@ -1033,6 +1058,8 @@ struct ath11k_base { DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT); } fw; + struct completion restart_completed; + #ifdef CONFIG_NL80211_TESTMODE struct { u32 data_pos; @@ -1041,6 +1068,10 @@ struct ath11k_base { } testmode; #endif + enum ath11k_pm_policy pm_policy; + enum ath11k_pm_policy actual_pm_policy; + struct notifier_block pm_nb; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; @@ -1232,8 +1263,10 @@ void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); int ath11k_core_check_dt(struct ath11k_base *ath11k); int ath11k_core_check_smbios(struct ath11k_base *ab); void ath11k_core_halt(struct ath11k *ar); +int ath11k_core_resume_early(struct ath11k_base *ab); int ath11k_core_resume(struct ath11k_base *ab); int ath11k_core_suspend(struct ath11k_base *ab); +int ath11k_core_suspend_late(struct ath11k_base *ab); void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab); bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab); @@ -1305,4 +1338,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus) return "unknown"; } +void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab); + #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c new file mode 100644 index 000000000000..b8bad358cebe --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include <linux/devcoredump.h> +#include "hif.h" +#include "coredump.h" +#include "debug.h" + +enum +ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type) +{ + enum ath11k_fw_crash_dump_type dump_type; + + switch (type) { + case HOST_DDR_REGION_TYPE: + dump_type = FW_CRASH_DUMP_REMOTE_MEM_DATA; + break; + case M3_DUMP_REGION_TYPE: + dump_type = FW_CRASH_DUMP_M3_DUMP; + break; + case PAGEABLE_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_PAGEABLE_DATA; + break; + case BDF_MEM_REGION_TYPE: + case CALDB_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_NONE; + break; + default: + dump_type = FW_CRASH_DUMP_TYPE_MAX; + break; + } + + return dump_type; +} +EXPORT_SYMBOL(ath11k_coredump_get_dump_type); + +void ath11k_coredump_upload(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, dump_work); + + ath11k_info(ab, "Uploading coredump\n"); + /* dev_coredumpv() takes ownership of the buffer */ + dev_coredumpv(ab->dev, ab->dump_data, ab->ath11k_coredump_len, GFP_KERNEL); + ab->dump_data = NULL; +} + +void ath11k_coredump_collect(struct ath11k_base *ab) +{ + ath11k_hif_coredump_download(ab); +} diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h new file mode 100644 index 000000000000..3960d9385261 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef _ATH11K_COREDUMP_H_ +#define _ATH11K_COREDUMP_H_ + +#define ATH11K_FW_CRASH_DUMP_V2 2 + +enum ath11k_fw_crash_dump_type { + FW_CRASH_DUMP_PAGING_DATA, + FW_CRASH_DUMP_RDDM_DATA, + FW_CRASH_DUMP_REMOTE_MEM_DATA, + FW_CRASH_DUMP_PAGEABLE_DATA, + FW_CRASH_DUMP_M3_DUMP, + FW_CRASH_DUMP_NONE, + + /* keep last */ + FW_CRASH_DUMP_TYPE_MAX, +}; + +#define COREDUMP_TLV_HDR_SIZE 8 + +struct ath11k_tlv_dump_data { + /* see ath11k_fw_crash_dump_type above */ + __le32 type; + + /* in bytes */ + __le32 tlv_len; + + /* pad to 32-bit boundaries as needed */ + u8 tlv_data[]; +} __packed; + +struct ath11k_dump_file_data { + /* "ATH11K-FW-DUMP" */ + char df_magic[16]; + /* total dump len in bytes */ + __le32 len; + /* file dump version */ + __le32 version; + /* pci device id */ + __le32 chip_id; + /* qrtr instance id */ + __le32 qrtr_id; + /* pci domain id */ + __le32 bus_id; + guid_t guid; + /* time-of-day stamp */ + __le64 tv_sec; + /* time-of-day stamp, nano-seconds */ + __le64 tv_nsec; + /* room for growth w/out changing binary format */ + u8 unused[128]; + u8 data[]; +} __packed; + +#ifdef CONFIG_DEV_COREDUMP +enum ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type); +void ath11k_coredump_upload(struct work_struct *work); +void ath11k_coredump_collect(struct ath11k_base *ab); +#else +static inline enum +ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type) +{ + return FW_CRASH_DUMP_TYPE_MAX; +} + +static inline void ath11k_coredump_upload(struct work_struct *work) +{ +} + +static inline void ath11k_coredump_collect(struct ath11k_base *ab) +{ +} +#endif + +#endif diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index a48e737ef35d..5d46f8e4c231 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/vmalloc.h> @@ -93,57 +93,14 @@ void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, spin_unlock_bh(&dbr_data->lock); } -static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar) -{ - spin_lock_bh(&ar->data_lock); - ar->fw_stats_done = false; - ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs); - ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs); - spin_unlock_bh(&ar->data_lock); -} - void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats) { struct ath11k_base *ab = ar->ab; - struct ath11k_pdev *pdev; - bool is_end; - static unsigned int num_vdev, num_bcn; - size_t total_vdevs_started = 0; - int i; - - /* WMI_REQUEST_PDEV_STAT request has been already processed */ - - if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) { - ar->fw_stats_done = true; - return; - } - - if (stats->stats_id == WMI_REQUEST_VDEV_STAT) { - if (list_empty(&stats->vdevs)) { - ath11k_warn(ab, "empty vdev stats"); - return; - } - /* FW sends all the active VDEV stats irrespective of PDEV, - * hence limit until the count of all VDEVs started - */ - for (i = 0; i < ab->num_radios; i++) { - pdev = rcu_dereference(ab->pdevs_active[i]); - if (pdev && pdev->ar) - total_vdevs_started += ar->num_started_vdevs; - } - - is_end = ((++num_vdev) == total_vdevs_started); - - list_splice_tail_init(&stats->vdevs, - &ar->fw_stats.vdevs); - - if (is_end) { - ar->fw_stats_done = true; - num_vdev = 0; - } - return; - } + bool is_end = true; + /* WMI_REQUEST_PDEV_STAT, WMI_REQUEST_RSSI_PER_CHAIN_STAT and + * WMI_REQUEST_VDEV_STAT requests have been already processed. + */ if (stats->stats_id == WMI_REQUEST_BCN_STAT) { if (list_empty(&stats->bcn)) { ath11k_warn(ab, "empty bcn stats"); @@ -152,97 +109,18 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats * /* Mark end until we reached the count of all started VDEVs * within the PDEV */ - is_end = ((++num_bcn) == ar->num_started_vdevs); + if (ar->num_started_vdevs) + is_end = ((++ar->fw_stats.num_bcn_recvd) == + ar->num_started_vdevs); list_splice_tail_init(&stats->bcn, &ar->fw_stats.bcn); - if (is_end) { - ar->fw_stats_done = true; - num_bcn = 0; - } + if (is_end) + complete(&ar->fw_stats_done); } } -static int ath11k_debugfs_fw_stats_request(struct ath11k *ar, - struct stats_request_params *req_param) -{ - struct ath11k_base *ab = ar->ab; - unsigned long timeout, time_left; - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - /* FW stats can get split when exceeding the stats data buffer limit. - * In that case, since there is no end marking for the back-to-back - * received 'update stats' event, we keep a 3 seconds timeout in case, - * fw_stats_done is not marked yet - */ - timeout = jiffies + msecs_to_jiffies(3 * 1000); - - ath11k_debugfs_fw_stats_reset(ar); - - reinit_completion(&ar->fw_stats_complete); - - ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); - - if (ret) { - ath11k_warn(ab, "could not request fw stats (%d)\n", - ret); - return ret; - } - - time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); - - if (!time_left) - return -ETIMEDOUT; - - for (;;) { - if (time_after(jiffies, timeout)) - break; - - spin_lock_bh(&ar->data_lock); - if (ar->fw_stats_done) { - spin_unlock_bh(&ar->data_lock); - break; - } - spin_unlock_bh(&ar->data_lock); - } - return 0; -} - -int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, - u32 vdev_id, u32 stats_id) -{ - struct ath11k_base *ab = ar->ab; - struct stats_request_params req_param; - int ret; - - mutex_lock(&ar->conf_mutex); - - if (ar->state != ATH11K_STATE_ON) { - ret = -ENETDOWN; - goto err_unlock; - } - - req_param.pdev_id = pdev_id; - req_param.vdev_id = vdev_id; - req_param.stats_id = stats_id; - - ret = ath11k_debugfs_fw_stats_request(ar, &req_param); - if (ret) - ath11k_warn(ab, "failed to request fw stats: %d\n", ret); - - ath11k_dbg(ab, ATH11K_DBG_WMI, - "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n", - pdev_id, vdev_id, stats_id); - -err_unlock: - mutex_unlock(&ar->conf_mutex); - - return ret; -} - static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) { struct ath11k *ar = inode->i_private; @@ -268,7 +146,7 @@ static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) req_param.vdev_id = 0; req_param.stats_id = WMI_REQUEST_PDEV_STAT; - ret = ath11k_debugfs_fw_stats_request(ar, &req_param); + ret = ath11k_mac_fw_stats_request(ar, &req_param); if (ret) { ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret); goto err_free; @@ -339,7 +217,7 @@ static int ath11k_open_vdev_stats(struct inode *inode, struct file *file) req_param.vdev_id = 0; req_param.stats_id = WMI_REQUEST_VDEV_STAT; - ret = ath11k_debugfs_fw_stats_request(ar, &req_param); + ret = ath11k_mac_fw_stats_request(ar, &req_param); if (ret) { ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); goto err_free; @@ -415,7 +293,7 @@ static int ath11k_open_bcn_stats(struct inode *inode, struct file *file) continue; req_param.vdev_id = arvif->vdev_id; - ret = ath11k_debugfs_fw_stats_request(ar, &req_param); + ret = ath11k_mac_fw_stats_request(ar, &req_param); if (ret) { ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); goto err_free; @@ -668,7 +546,7 @@ static ssize_t ath11k_write_extd_rx_stats(struct file *file, ar->debug.rx_filter = tlv_filter.rx_filter; - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, HAL_RXDMA_MONITOR_STATUS, @@ -980,7 +858,7 @@ int ath11k_debugfs_pdev_create(struct ath11k_base *ab) debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, &fops_simulate_fw_crash); - debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab, + debugfs_create_file("soc_dp_stats", 0400, ab->debugfs_soc, ab, &fops_soc_dp_stats); if (ab->hw_params.sram_dump.start != 0) @@ -1112,7 +990,7 @@ static ssize_t ath11k_write_pktlog_filter(struct file *file, } /* Clear rx filter set for monitor mode and rx status */ - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, HAL_RXDMA_MONITOR_STATUS, @@ -1171,7 +1049,7 @@ static ssize_t ath11k_write_pktlog_filter(struct file *file, HTT_RX_FP_DATA_FILTER_FLASG3; } - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id, ar->dp.mac_id + i, diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index a39e458637b0..ed7fec177588 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.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-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH11K_DEBUGFS_H_ @@ -273,8 +273,6 @@ void ath11k_debugfs_unregister(struct ath11k *ar); void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats); void ath11k_debugfs_fw_stats_init(struct ath11k *ar); -int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, - u32 vdev_id, u32 stats_id); static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar) { @@ -381,12 +379,6 @@ static inline int ath11k_debugfs_rx_filter(struct ath11k *ar) return 0; } -static inline int ath11k_debugfs_get_fw_stats(struct ath11k *ar, - u32 pdev_id, u32 vdev_id, u32 stats_id) -{ - return 0; -} - static inline void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, enum wmi_direct_buffer_module id, diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 1a62407e5a9f..bf3928ada995 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <crypto/hash.h> @@ -104,14 +104,12 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) if (!ring->vaddr_unaligned) return; - if (ring->cached) { - dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size, - DMA_FROM_DEVICE); - kfree(ring->vaddr_unaligned); - } else { + if (ring->cached) + dma_free_noncoherent(ab->dev, ring->size, ring->vaddr_unaligned, + ring->paddr_unaligned, DMA_FROM_DEVICE); + else dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, ring->paddr_unaligned); - } ring->vaddr_unaligned = NULL; } @@ -249,25 +247,14 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, default: cached = false; } - - if (cached) { - ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL); - if (!ring->vaddr_unaligned) - return -ENOMEM; - - ring->paddr_unaligned = dma_map_single(ab->dev, - ring->vaddr_unaligned, - ring->size, - DMA_FROM_DEVICE); - if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) { - kfree(ring->vaddr_unaligned); - ring->vaddr_unaligned = NULL; - return -ENOMEM; - } - } } - if (!cached) + if (cached) + ring->vaddr_unaligned = dma_alloc_noncoherent(ab->dev, ring->size, + &ring->paddr_unaligned, + DMA_FROM_DEVICE, + GFP_KERNEL); + else ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, &ring->paddr_unaligned, GFP_KERNEL); @@ -830,8 +817,8 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, if (ab->hw_params.ring_mask->rx_mon_status[grp_id]) { for (i = 0; i < ab->num_radios; i++) { - for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { - int id = i * ab->hw_params.num_rxmda_per_pdev + j; + for (j = 0; j < ab->hw_params.num_rxdma_per_pdev; j++) { + int id = i * ab->hw_params.num_rxdma_per_pdev + j; if (ab->hw_params.ring_mask->rx_mon_status[grp_id] & BIT(id)) { @@ -853,8 +840,8 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, ath11k_dp_process_reo_status(ab); for (i = 0; i < ab->num_radios; i++) { - for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { - int id = i * ab->hw_params.num_rxmda_per_pdev + j; + for (j = 0; j < ab->hw_params.num_rxdma_per_pdev; j++) { + int id = i * ab->hw_params.num_rxdma_per_pdev + j; if (ab->hw_params.ring_mask->rxdma2host[grp_id] & BIT(id)) { work_done = ath11k_dp_process_rxdma_err(ab, id, budget); @@ -888,7 +875,7 @@ void ath11k_dp_pdev_free(struct ath11k_base *ab) struct ath11k *ar; int i; - del_timer_sync(&ab->mon_reap_timer); + timer_delete_sync(&ab->mon_reap_timer); for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -913,7 +900,7 @@ void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) spin_lock_init(&dp->rx_refill_buf_ring.idr_lock); atomic_set(&dp->num_tx_pending, 0); init_waitqueue_head(&dp->tx_empty_waitq); - for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + for (j = 0; j < ab->hw_params.num_rxdma_per_pdev; j++) { idr_init(&dp->rx_mon_status_refill_ring[j].bufs_idr); spin_lock_init(&dp->rx_mon_status_refill_ring[j].idr_lock); } @@ -1130,8 +1117,9 @@ fail_link_desc_cleanup: static void ath11k_dp_shadow_timer_handler(struct timer_list *t) { - struct ath11k_hp_update_timer *update_timer = from_timer(update_timer, - t, timer); + struct ath11k_hp_update_timer *update_timer = timer_container_of(update_timer, + t, + timer); struct ath11k_base *ab = update_timer->ab; struct hal_srng *srng = &ab->hal.srng_list[update_timer->ring_id]; @@ -1183,7 +1171,7 @@ void ath11k_dp_shadow_stop_timer(struct ath11k_base *ab, if (!update_timer->init) return; - del_timer_sync(&update_timer->timer); + timer_delete_sync(&update_timer->timer); } void ath11k_dp_shadow_init_timer(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 2f6dd69d3be2..7a55afd33be8 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_H @@ -20,7 +20,6 @@ struct ath11k_ext_irq_grp; struct dp_rx_tid { u8 tid; - u32 *vaddr; dma_addr_t paddr; u32 size; u32 ba_win_sz; @@ -37,6 +36,9 @@ struct dp_rx_tid { /* Timer info related to fragments */ struct timer_list frag_timer; struct ath11k_base *ab; + u32 *vaddr_unaligned; + dma_addr_t paddr_unaligned; + u32 unaligned_size; }; #define DP_REO_DESC_FREE_THRESHOLD 64 @@ -165,7 +167,6 @@ struct ath11k_mon_data { struct ath11k_pdev_mon_stats rx_mon_stats; /* lock for monitor data */ spinlock_t mon_lock; - struct sk_buff_head rx_status_q; }; struct ath11k_pdev_dp { @@ -1305,18 +1306,6 @@ struct htt_ppdu_stats_user_rate { #define HTT_TX_INFO_PEERID(_flags) \ FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M, _flags) -struct htt_tx_ppdu_stats_info { - struct htt_tlv tlv_hdr; - u32 tx_success_bytes; - u32 tx_retry_bytes; - u32 tx_failed_bytes; - u32 flags; /* %HTT_PPDU_STATS_TX_INFO_FLAGS_ */ - u16 tx_success_msdus; - u16 tx_retry_msdus; - u16 tx_failed_msdus; - u16 tx_duration; /* united in us */ -} __packed; - enum htt_ppdu_stats_usr_compln_status { HTT_PPDU_STATS_USER_STATUS_OK, HTT_PPDU_STATS_USER_STATUS_FILTERED, @@ -1364,17 +1353,6 @@ struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { u32 success_bytes; } __packed; -struct htt_ppdu_stats_usr_cmn_array { - struct htt_tlv tlv_hdr; - u32 num_ppdu_stats; - /* tx_ppdu_stats_info is filled by multiple struct htt_tx_ppdu_stats_info - * elements. - * tx_ppdu_stats_info is variable length, with length = - * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) - */ - struct htt_tx_ppdu_stats_info tx_ppdu_info[]; -} __packed; - struct htt_ppdu_user_stats { u16 peer_id; u32 tlv_flags; diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index afd481f5858f..9230a965f6f0 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/ieee80211.h> @@ -308,10 +308,10 @@ static u8 *ath11k_dp_rxdesc_mpdu_start_addr2(struct ath11k_base *ab, static void ath11k_dp_service_mon_ring(struct timer_list *t) { - struct ath11k_base *ab = from_timer(ab, t, mon_reap_timer); + struct ath11k_base *ab = timer_container_of(ab, t, mon_reap_timer); int i; - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) ath11k_dp_rx_process_mon_rings(ab, i, NULL, DP_MON_SERVICE_BUDGET); mod_timer(&ab->mon_reap_timer, jiffies + @@ -324,7 +324,7 @@ static int ath11k_dp_purge_mon_ring(struct ath11k_base *ab) unsigned long timeout = jiffies + msecs_to_jiffies(DP_MON_PURGE_TIMEOUT_MS); do { - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) reaped += ath11k_dp_rx_process_mon_rings(ab, i, NULL, DP_MON_SERVICE_BUDGET); @@ -468,7 +468,7 @@ static int ath11k_dp_rxdma_pdev_buf_free(struct ath11k *ar) rx_ring = &dp->rxdma_mon_buf_ring; ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { rx_ring = &dp->rx_mon_status_refill_ring[i]; ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); } @@ -506,7 +506,7 @@ static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar) ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF); } - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { rx_ring = &dp->rx_mon_status_refill_ring[i]; ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS); } @@ -522,7 +522,7 @@ static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) ath11k_dp_srng_cleanup(ab, &dp->rx_refill_buf_ring.refill_buf_ring); - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { if (ab->hw_params.rx_mac_buf_ring) ath11k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]); @@ -585,7 +585,7 @@ static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) } if (ar->ab->hw_params.rx_mac_buf_ring) { - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ret = ath11k_dp_srng_setup(ar->ab, &dp->rx_mac_buf_ring[i], HAL_RXDMA_BUF, 1, @@ -598,7 +598,7 @@ static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) } } - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring[i], HAL_RXDMA_DST, 0, dp->mac_id + i, DP_RXDMA_ERR_DST_RING_SIZE); @@ -608,7 +608,7 @@ static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) } } - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; ret = ath11k_dp_srng_setup(ar->ab, srng, @@ -675,11 +675,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); rx_tid = &cmd->data; - if (rx_tid->vaddr) { - dma_unmap_single(ab->dev, rx_tid->paddr, - rx_tid->size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } kfree(cmd); } @@ -689,11 +689,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; rx_tid = &cmd_cache->data; - if (rx_tid->vaddr) { - dma_unmap_single(ab->dev, rx_tid->paddr, - rx_tid->size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } kfree(cmd_cache); } @@ -708,11 +708,11 @@ static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, if (status != HAL_REO_CMD_SUCCESS) ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - if (rx_tid->vaddr) { - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(dp->ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } } @@ -749,10 +749,10 @@ static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, if (ret) { ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", rx_tid->tid, ret); - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } } @@ -802,10 +802,10 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, return; free_desc: - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } void ath11k_peer_rx_tid_delete(struct ath11k *ar, @@ -831,14 +831,16 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar, if (ret != -ESHUTDOWN) ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", tid, ret); - dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ar->ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } rx_tid->paddr = 0; + rx_tid->paddr_unaligned = 0; rx_tid->size = 0; + rx_tid->unaligned_size = 0; } static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, @@ -904,7 +906,7 @@ void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer) rx_tid = &peer->rx_tid[i]; spin_unlock_bh(&ar->ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ar->ab->base_lock); ath11k_dp_rx_frags_cleanup(rx_tid, true); @@ -925,7 +927,7 @@ void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) ath11k_dp_rx_frags_cleanup(rx_tid, true); spin_unlock_bh(&ar->ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ar->ab->base_lock); } } @@ -982,10 +984,9 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, if (!rx_tid->active) goto unlock_exit; - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; rx_tid->active = false; @@ -1000,9 +1001,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, struct ath11k_base *ab = ar->ab; struct ath11k_peer *peer; struct dp_rx_tid *rx_tid; - u32 hw_desc_sz; - u32 *addr_aligned; - void *vaddr; + u32 hw_desc_sz, *vaddr; + void *vaddr_unaligned; dma_addr_t paddr; int ret; @@ -1050,37 +1050,34 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, else hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); - vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); - if (!vaddr) { + rx_tid->unaligned_size = hw_desc_sz + HAL_LINK_DESC_ALIGN - 1; + vaddr_unaligned = dma_alloc_noncoherent(ab->dev, rx_tid->unaligned_size, &paddr, + DMA_BIDIRECTIONAL, GFP_ATOMIC); + if (!vaddr_unaligned) { spin_unlock_bh(&ab->base_lock); return -ENOMEM; } - addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - - ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, - ssn, pn_type); - - paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); - - ret = dma_mapping_error(ab->dev, paddr); - if (ret) { - spin_unlock_bh(&ab->base_lock); - ath11k_warn(ab, "failed to setup dma map for peer %pM rx tid %d: %d\n", - peer_mac, tid, ret); - goto err_mem_free; - } - - rx_tid->vaddr = vaddr; - rx_tid->paddr = paddr; + rx_tid->vaddr_unaligned = vaddr_unaligned; + vaddr = PTR_ALIGN(vaddr_unaligned, HAL_LINK_DESC_ALIGN); + rx_tid->paddr_unaligned = paddr; + rx_tid->paddr = rx_tid->paddr_unaligned + ((unsigned long)vaddr - + (unsigned long)rx_tid->vaddr_unaligned); + ath11k_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type); rx_tid->size = hw_desc_sz; rx_tid->active = true; + /* After dma_alloc_noncoherent, vaddr is being modified for reo qdesc setup. + * Since these changes are not reflected in the device, driver now needs to + * explicitly call dma_sync_single_for_device. + */ + dma_sync_single_for_device(ab->dev, rx_tid->paddr, + rx_tid->size, + DMA_TO_DEVICE); spin_unlock_bh(&ab->base_lock); - ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, ba_win_sz); + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, rx_tid->paddr, + tid, 1, ba_win_sz); if (ret) { ath11k_warn(ar->ab, "failed to setup rx reorder queue for peer %pM tid %d: %d\n", peer_mac, tid, ret); @@ -1088,12 +1085,6 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, } return ret; - -err_mem_free: - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; - - return ret; } int ath11k_dp_rx_ampdu_start(struct ath11k *ar, @@ -1877,8 +1868,7 @@ static void ath11k_dp_rx_h_csum_offload(struct ath11k *ar, struct sk_buff *msdu) CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, - enum hal_encrypt_type enctype) +int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, enum hal_encrypt_type enctype) { switch (enctype) { case HAL_ENCRYPT_TYPE_OPEN: @@ -2647,7 +2637,7 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, struct ath11k *ar; struct hal_reo_dest_ring *desc; enum hal_reo_dest_ring_push_reason push_reason; - u32 cookie; + u32 cookie, info0, rx_msdu_info0, rx_mpdu_info0; int i; for (i = 0; i < MAX_RADIOS; i++) @@ -2660,11 +2650,14 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, try_again: ath11k_hal_srng_access_begin(ab, srng); + /* Make sure descriptor is read after the head pointer. */ + dma_rmb(); + while (likely(desc = (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, srng))) { cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, - desc->buf_addr_info.info1); + READ_ONCE(desc->buf_addr_info.info1)); buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, cookie); @@ -2693,27 +2686,31 @@ try_again: num_buffs_reaped[mac_id]++; + info0 = READ_ONCE(desc->info0); push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, - desc->info0); + info0); if (unlikely(push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) { dev_kfree_skb_any(msdu); - ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++; + ab->soc_stats.hal_reo_error[ring_id]++; continue; } - rxcb->is_first_msdu = !!(desc->rx_msdu_info.info0 & + rx_msdu_info0 = READ_ONCE(desc->rx_msdu_info.info0); + rx_mpdu_info0 = READ_ONCE(desc->rx_mpdu_info.info0); + + rxcb->is_first_msdu = !!(rx_msdu_info0 & RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - rxcb->is_last_msdu = !!(desc->rx_msdu_info.info0 & + rxcb->is_last_msdu = !!(rx_msdu_info0 & RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - rxcb->is_continuation = !!(desc->rx_msdu_info.info0 & + rxcb->is_continuation = !!(rx_msdu_info0 & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); rxcb->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID, - desc->rx_mpdu_info.meta_data); + READ_ONCE(desc->rx_mpdu_info.meta_data)); rxcb->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, - desc->rx_mpdu_info.info0); + rx_mpdu_info0); rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM, - desc->info0); + info0); rxcb->mac_id = mac_id; __skb_queue_tail(&msdu_list[mac_id], msdu); @@ -2832,8 +2829,6 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, rx_stats->dcm_count += ppdu_info->dcm; rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; - arsta->rssi_comb = ppdu_info->rssi_comb; - BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); @@ -2990,11 +2985,52 @@ ath11k_dp_rx_mon_update_status_buf_state(struct ath11k_mon_data *pmon, } } +static enum dp_mon_status_buf_state +ath11k_dp_rx_mon_buf_done(struct ath11k_base *ab, struct hal_srng *srng, + struct dp_rxdma_ring *rx_ring) +{ + struct ath11k_skb_rxcb *rxcb; + struct hal_tlv_hdr *tlv; + struct sk_buff *skb; + void *status_desc; + dma_addr_t paddr; + u32 cookie; + int buf_id; + u8 rbm; + + status_desc = ath11k_hal_srng_src_next_peek(ab, srng); + if (!status_desc) + return DP_MON_STATUS_NO_DMA; + + ath11k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm); + + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + if (!skb) + return DP_MON_STATUS_NO_DMA; + + rxcb = ATH11K_SKB_RXCB(skb); + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + tlv = (struct hal_tlv_hdr *)skb->data; + if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != HAL_RX_STATUS_BUFFER_DONE) + return DP_MON_STATUS_NO_DMA; + + return DP_MON_STATUS_REPLINISH; +} + static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, int *budget, struct sk_buff_head *skb_list) { struct ath11k *ar; const struct ath11k_hw_hal_params *hal_params; + enum dp_mon_status_buf_state reap_status; struct ath11k_pdev_dp *dp; struct dp_rxdma_ring *rx_ring; struct ath11k_mon_data *pmon; @@ -3057,15 +3093,38 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, ath11k_warn(ab, "mon status DONE not set %lx, buf_id %d\n", FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl), buf_id); - /* If done status is missing, hold onto status - * ring until status is done for this status - * ring buffer. - * Keep HP in mon_status_ring unchanged, - * and break from here. - * Check status for same buffer for next time + /* RxDMA status done bit might not be set even + * though tp is moved by HW. */ - pmon->buf_state = DP_MON_STATUS_NO_DMA; - break; + + /* If done status is missing: + * 1. As per MAC team's suggestion, + * when HP + 1 entry is peeked and if DMA + * is not done and if HP + 2 entry's DMA done + * is set. skip HP + 1 entry and + * start processing in next interrupt. + * 2. If HP + 2 entry's DMA done is not set, + * poll onto HP + 1 entry DMA done to be set. + * Check status for same buffer for next time + * dp_rx_mon_status_srng_process + */ + + reap_status = ath11k_dp_rx_mon_buf_done(ab, srng, + rx_ring); + if (reap_status == DP_MON_STATUS_NO_DMA) + continue; + + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + dev_kfree_skb_any(skb); + pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; } spin_lock_bh(&rx_ring->idr_lock); @@ -3115,7 +3174,8 @@ move_next: static void ath11k_dp_rx_frag_timer(struct timer_list *timer) { - struct dp_rx_tid *rx_tid = from_timer(rx_tid, timer, frag_timer); + struct dp_rx_tid *rx_tid = timer_container_of(rx_tid, timer, + frag_timer); spin_lock_bh(&rx_tid->ab->base_lock); if (rx_tid->last_frag_no && @@ -3658,7 +3718,7 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, } spin_unlock_bh(&ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ab->base_lock); peer = ath11k_peer_find_by_id(ab, peer_id); @@ -3809,6 +3869,7 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, &rbm); if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && + rbm != HAL_RX_BUF_RBM_SW1_BM && rbm != HAL_RX_BUF_RBM_SW3_BM) { ab->soc_stats.invalid_rbm++; ath11k_warn(ab, "invalid return buffer manager %d\n", rbm); @@ -4391,7 +4452,7 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) } if (ab->hw_params.rx_mac_buf_ring) { - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = dp->rx_mac_buf_ring[i].ring_id; ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id + i, HAL_RXDMA_BUF); @@ -4403,7 +4464,7 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) } } - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = dp->rxdma_err_dst_ring[i].ring_id; ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id + i, HAL_RXDMA_DST); @@ -4443,7 +4504,7 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) } config_refill_ring: - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id + i, HAL_RXDMA_MONITOR_STATUS); @@ -4627,11 +4688,12 @@ static void ath11k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info, } } -static u32 -ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id, - void *ring_entry, struct sk_buff **head_msdu, - struct sk_buff **tail_msdu, u32 *npackets, - u32 *ppdu_id) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +u32 ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id, + void *ring_entry, struct sk_buff **head_msdu, + struct sk_buff **tail_msdu, u32 *npackets, + u32 *ppdu_id) { struct ath11k_pdev_dp *dp = &ar->dp; struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; @@ -4718,7 +4780,7 @@ ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id, if (!msdu) { ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "msdu_pop: invalid buf_id %d\n", buf_id); - break; + goto next_msdu; } rxcb = ATH11K_SKB_RXCB(msdu); if (!rxcb->unmapped) { @@ -5083,7 +5145,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; const struct ath11k_hw_hal_params *hal_params; void *ring_entry; - void *mon_dst_srng; + struct hal_srng *mon_dst_srng; u32 ppdu_id; u32 rx_bufs_used; u32 ring_id; @@ -5100,6 +5162,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, spin_lock_bh(&pmon->mon_lock); + spin_lock_bh(&mon_dst_srng->lock); ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); ppdu_id = pmon->mon_ppdu_info.ppdu_id; @@ -5158,6 +5221,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, mon_dst_srng); } ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + spin_unlock_bh(&mon_dst_srng->lock); spin_unlock_bh(&pmon->mon_lock); @@ -5228,8 +5292,11 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, hal_status == HAL_TLV_STATUS_PPDU_DONE) { rx_mon_stats->status_ppdu_done++; pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; - ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); - pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + if (!ab->hw_params.full_monitor_mode) { + ath11k_dp_rx_mon_dest_process(ar, mac_id, + budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } } if (ppdu_info->peer_id == HAL_INVALID_PEERID || @@ -5342,7 +5409,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, "full mon msdu_pop: invalid buf_id %d\n", buf_id); spin_unlock_bh(&rx_ring->idr_lock); - break; + goto next_msdu; } idr_remove(&rx_ring->bufs_idr, buf_id); spin_unlock_bh(&rx_ring->idr_lock); @@ -5544,7 +5611,7 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, struct hal_sw_mon_ring_entries *sw_mon_entries; struct ath11k_pdev_mon_stats *rx_mon_stats; struct sk_buff *head_msdu, *tail_msdu; - void *mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + struct hal_srng *mon_dst_srng; void *ring_entry; u32 rx_bufs_used = 0, mpdu_rx_bufs_used; int quota = 0, ret; @@ -5560,6 +5627,9 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, goto reap_status_ring; } + mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + spin_lock_bh(&mon_dst_srng->lock); + ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { head_msdu = NULL; @@ -5603,6 +5673,7 @@ next_entry: } ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + spin_unlock_bh(&mon_dst_srng->lock); spin_unlock_bh(&pmon->mon_lock); if (rx_bufs_used) { @@ -5639,8 +5710,6 @@ static int ath11k_dp_rx_pdev_mon_status_attach(struct ath11k *ar) struct ath11k_pdev_dp *dp = &ar->dp; struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; - skb_queue_head_init(&pmon->rx_status_q); - pmon->mon_ppdu_status = DP_PPDU_STATUS_START; memset(&pmon->rx_mon_stats, 0, @@ -5720,7 +5789,7 @@ int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer) int ret; if (stop_timer) - del_timer_sync(&ab->mon_reap_timer); + timer_delete_sync(&ab->mon_reap_timer); /* reap all the monitor related rings */ ret = ath11k_dp_purge_mon_ring(ab); diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h index 623da3bf9dc8..c322e30caa96 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.h +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_RX_H #define ATH11K_DP_RX_H @@ -95,4 +96,6 @@ int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id int ath11k_dp_rx_pktlog_start(struct ath11k_base *ab); int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer); +int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, enum hal_encrypt_type enctype); + #endif /* ATH11K_DP_RX_H */ diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 272b1c35f98d..8522c67baabf 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -353,8 +353,12 @@ ath11k_dp_tx_htt_tx_complete_buf(struct ath11k_base *ab, if (ts->acked) { if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + - ts->ack_rssi; + info->status.ack_signal = ts->ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) + info->status.ack_signal += ATH11K_DEFAULT_NOISE_FLOOR; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } else { @@ -584,8 +588,12 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + - ts->ack_rssi; + info->status.ack_signal = ts->ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) + info->status.ack_signal += ATH11K_DEFAULT_NOISE_FLOOR; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } @@ -1035,7 +1043,7 @@ int ath11k_dp_tx_htt_h2t_ppdu_stats_req(struct ath11k *ar, u32 mask) int ret; int i; - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { skb = ath11k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; @@ -1218,7 +1226,7 @@ int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset) &tlv_filter); } else if (!reset) { /* set in monitor mode only */ - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = dp->rx_mac_buf_ring[i].ring_id; ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id + i, @@ -1231,7 +1239,7 @@ int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset) if (ret) return ret; - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; if (!reset) { tlv_filter.rx_filter = diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h index 61be2265e09f..795fe3b8fa0d 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.h +++ b/drivers/net/wireless/ath/ath11k/dp_tx.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, 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_TX_H @@ -13,7 +13,7 @@ struct ath11k_dp_htt_wbm_tx_status { u32 msdu_id; bool acked; - int ack_rssi; + s8 ack_rssi; u16 peer_id; }; diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c index 4e36292a79db..cbbd8e57119f 100644 --- a/drivers/net/wireless/ath/ath11k/fw.c +++ b/drivers/net/wireless/ath/ath11k/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -166,3 +166,4 @@ void ath11k_fw_destroy(struct ath11k_base *ab) { release_firmware(ab->fw.fw); } +EXPORT_SYMBOL(ath11k_fw_destroy); diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index f3d04568c221..8cb1505a5a0c 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/dma-mapping.h> #include "hal_tx.h" @@ -599,7 +599,7 @@ u32 ath11k_hal_ce_dst_status_get_length(void *buf) struct hal_ce_srng_dst_status_desc *desc = buf; u32 len; - len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, desc->flags); + len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, READ_ONCE(desc->flags)); desc->flags &= ~HAL_CE_DST_STATUS_DESC_FLAGS_LEN; return len; @@ -796,6 +796,20 @@ u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, return desc; } +u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab, struct hal_srng *srng) +{ + u32 next_hp; + + lockdep_assert_held(&srng->lock); + + next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; + + if (next_hp != srng->u.src_ring.cached_tp) + return srng->ring_base_vaddr + next_hp; + + return NULL; +} + u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng) { lockdep_assert_held(&srng->lock); @@ -815,7 +829,7 @@ void ath11k_hal_srng_access_begin(struct ath11k_base *ab, struct hal_srng *srng) srng->u.src_ring.cached_tp = *(volatile u32 *)srng->u.src_ring.tp_addr; } else { - srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = READ_ONCE(*srng->u.dst_ring.hp_addr); /* Try to prefetch the next descriptor in the ring */ if (srng->flags & HAL_SRNG_FLAGS_CACHED) @@ -1337,6 +1351,7 @@ void ath11k_hal_srng_deinit(struct ath11k_base *ab) ath11k_hal_free_cont_rdp(ab); ath11k_hal_free_cont_wrp(ab); kfree(hal->srng_config); + hal->srng_config = NULL; } EXPORT_SYMBOL(ath11k_hal_srng_deinit); diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h index 65e8f244ebb9..601542410c75 100644 --- a/drivers/net/wireless/ath/ath11k/hal.h +++ b/drivers/net/wireless/ath/ath11k/hal.h @@ -664,7 +664,7 @@ struct hal_srng_config { }; /** - * enum hal_rx_buf_return_buf_manager + * enum hal_rx_buf_return_buf_manager - manager for returned rx buffers * * @HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST: Buffer returned to WBM idle buffer list * @HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST: Descriptor returned to WBM idle @@ -700,7 +700,7 @@ enum hal_rx_buf_return_buf_manager { #define HAL_REO_CMD_FLG_UNBLK_RESOURCE BIT(7) #define HAL_REO_CMD_FLG_UNBLK_CACHE BIT(8) -/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* feilds */ +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* fields */ #define HAL_REO_CMD_UPD0_RX_QUEUE_NUM BIT(8) #define HAL_REO_CMD_UPD0_VLD BIT(9) #define HAL_REO_CMD_UPD0_ALDC BIT(10) @@ -725,7 +725,7 @@ enum hal_rx_buf_return_buf_manager { #define HAL_REO_CMD_UPD0_PN_VALID BIT(29) #define HAL_REO_CMD_UPD0_PN BIT(30) -/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* feilds */ +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* fields */ #define HAL_REO_CMD_UPD1_VLD BIT(16) #define HAL_REO_CMD_UPD1_ALDC GENMASK(18, 17) #define HAL_REO_CMD_UPD1_DIS_DUP_DETECTION BIT(19) @@ -741,7 +741,7 @@ enum hal_rx_buf_return_buf_manager { #define HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE BIT(30) #define HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG BIT(31) -/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* feilds */ +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* fields */ #define HAL_REO_CMD_UPD2_SVLD BIT(10) #define HAL_REO_CMD_UPD2_SSN GENMASK(22, 11) #define HAL_REO_CMD_UPD2_SEQ_2K_ERR BIT(23) @@ -947,6 +947,8 @@ u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng); int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, bool sync_hw_ptr); u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng); +u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab, + struct hal_srng *srng); u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, struct hal_srng *srng); u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 8f7dd43dc1bd..753bd93f0212 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -372,7 +372,8 @@ int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, ret_buf_mgr = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, wbm_desc->buf_addr_info.info1); - if (ret_buf_mgr != HAL_RX_BUF_RBM_SW3_BM) { + if (ret_buf_mgr != HAL_RX_BUF_RBM_SW1_BM && + ret_buf_mgr != HAL_RX_BUF_RBM_SW3_BM) { ab->soc_stats.invalid_rbm++; return -EINVAL; } diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index c5e88364afe5..46d17abd808b 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_HAL_TX_H @@ -54,7 +54,7 @@ struct hal_tx_info { struct hal_tx_status { enum hal_wbm_rel_src_module buf_rel_source; enum hal_wbm_tqm_rel_reason status; - u8 ack_rssi; + s8 ack_rssi; u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ u32 ppdu_id; u8 try_cnt; diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index 877a4073fed6..cd9c4b838246 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _HIF_H_ @@ -18,7 +18,7 @@ struct ath11k_hif_ops { int (*start)(struct ath11k_base *ab); void (*stop)(struct ath11k_base *ab); int (*power_up)(struct ath11k_base *ab); - void (*power_down)(struct ath11k_base *ab); + void (*power_down)(struct ath11k_base *ab, bool is_suspend); int (*suspend)(struct ath11k_base *ab); int (*resume)(struct ath11k_base *ab); int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id, @@ -31,6 +31,7 @@ struct ath11k_hif_ops { void (*ce_irq_enable)(struct ath11k_base *ab); void (*ce_irq_disable)(struct ath11k_base *ab); void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); + void (*coredump_download)(struct ath11k_base *ab); }; static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) @@ -67,12 +68,18 @@ static inline void ath11k_hif_irq_disable(struct ath11k_base *ab) static inline int ath11k_hif_power_up(struct ath11k_base *ab) { + if (!ab->hif.ops->power_up) + return -EOPNOTSUPP; + return ab->hif.ops->power_up(ab); } -static inline void ath11k_hif_power_down(struct ath11k_base *ab) +static inline void ath11k_hif_power_down(struct ath11k_base *ab, bool is_suspend) { - ab->hif.ops->power_down(ab); + if (!ab->hif.ops->power_down) + return; + + ab->hif.ops->power_down(ab, is_suspend); } static inline int ath11k_hif_suspend(struct ath11k_base *ab) @@ -146,4 +153,10 @@ static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, *msi_data_idx = ce_id; } +static inline void ath11k_hif_coredump_download(struct ath11k_base *ab) +{ + if (ab->hif.ops->coredump_download) + ab->hif.ops->coredump_download(ab); +} + #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 14ef4eb48f80..52d9f4c13b13 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_HW_H @@ -167,7 +167,7 @@ struct ath11k_hw_params { bool single_pdev_only; bool rxdma1_enable; - int num_rxmda_per_pdev; + int num_rxdma_per_pdev; bool rx_mac_buf_ring; bool vdev_start_delay; bool htt_peer_map_v2; @@ -227,6 +227,7 @@ struct ath11k_hw_params { bool smp2p_wow_exit; bool support_fw_mac_sequence; bool support_dual_stations; + bool pdev_suspend; }; struct ath11k_hw_ops { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index a6a37d67a50a..13301ca317a5 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.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-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <net/mac80211.h> @@ -1231,14 +1231,7 @@ static int ath11k_mac_vif_setup_ps(struct ath11k_vif *arvif) enable_ps = arvif->ps; - if (!arvif->is_started) { - /* mac80211 can update vif powersave state while disconnected. - * Firmware doesn't behave nicely and consumes more power than - * necessary if PS is disabled on a non-started vdev. Hence - * force-enable PS for non-running vdevs. - */ - psmode = WMI_STA_PS_MODE_ENABLED; - } else if (enable_ps) { + if (enable_ps) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; @@ -1430,10 +1423,67 @@ static bool ath11k_mac_set_nontx_vif_params(struct ath11k_vif *tx_arvif, return false; } -static void ath11k_mac_set_vif_params(struct ath11k_vif *arvif, - struct sk_buff *bcn) +static int ath11k_mac_setup_bcn_p2p_ie(struct ath11k_vif *arvif, + struct sk_buff *bcn) { + struct ath11k *ar = arvif->ar; struct ieee80211_mgmt *mgmt; + const u8 *p2p_ie; + int ret; + + mgmt = (void *)bcn->data; + p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, + mgmt->u.beacon.variable, + bcn->len - (mgmt->u.beacon.variable - + bcn->data)); + if (!p2p_ie) + return -ENOENT; + + ret = ath11k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie); + if (ret) { + ath11k_warn(ar->ab, "failed to submit P2P GO bcn ie for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return ret; +} + +static int ath11k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui, + u8 oui_type, size_t ie_offset) +{ + size_t len; + const u8 *next, *end; + u8 *ie; + + if (WARN_ON(skb->len < ie_offset)) + return -EINVAL; + + ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type, + skb->data + ie_offset, + skb->len - ie_offset); + if (!ie) + return -ENOENT; + + len = ie[1] + 2; + end = skb->data + skb->len; + next = ie + len; + + if (WARN_ON(next > end)) + return -EINVAL; + + memmove(ie, next, end - next); + skb_trim(skb, skb->len - len); + + return 0; +} + +static int ath11k_mac_set_vif_params(struct ath11k_vif *arvif, + struct sk_buff *bcn) +{ + struct ath11k_base *ab = arvif->ar->ab; + struct ieee80211_mgmt *mgmt; + int ret = 0; u8 *ies; ies = bcn->data + ieee80211_get_hdrlen_from_skb(bcn); @@ -1451,19 +1501,57 @@ static void ath11k_mac_set_vif_params(struct ath11k_vif *arvif, arvif->wpaie_present = true; else arvif->wpaie_present = false; + + if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + return ret; + + ret = ath11k_mac_setup_bcn_p2p_ie(arvif, bcn); + if (ret) { + ath11k_warn(ab, "failed to setup P2P GO bcn ie: %d\n", + ret); + return ret; + } + + /* P2P IE is inserted by firmware automatically (as + * configured above) so remove it from the base beacon + * template to avoid duplicate P2P IEs in beacon frames. + */ + ret = ath11k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P, + offsetof(struct ieee80211_mgmt, + u.beacon.variable)); + if (ret) { + ath11k_warn(ab, "failed to remove P2P vendor ie: %d\n", + ret); + return ret; + } + + return ret; } -static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) +static struct ath11k_vif *ath11k_mac_get_tx_arvif(struct ath11k_vif *arvif) +{ + struct ieee80211_bss_conf *link_conf, *tx_bss_conf; + + lockdep_assert_wiphy(arvif->ar->hw->wiphy); + + link_conf = &arvif->vif->bss_conf; + tx_bss_conf = wiphy_dereference(arvif->ar->hw->wiphy, link_conf->tx_bss_conf); + if (tx_bss_conf) + return ath11k_vif_to_arvif(tx_bss_conf->vif); + + return NULL; +} + +static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif, + struct ath11k_vif *tx_arvif) { - struct ath11k_vif *tx_arvif; struct ieee80211_ema_beacons *beacons; int ret = 0; bool nontx_vif_params_set = false; u32 params = 0; u8 i = 0; - tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); - beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw, tx_arvif->vif, 0); if (!beacons || !beacons->cnt) { @@ -1472,10 +1560,12 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) return -EPERM; } - if (tx_arvif == arvif) - ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb); - else + if (tx_arvif == arvif) { + if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) + return -EINVAL; + } else { arvif->wpaie_present = tx_arvif->wpaie_present; + } for (i = 0; i < beacons->cnt; i++) { if (tx_arvif != arvif && !nontx_vif_params_set) @@ -1507,25 +1597,22 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) return ret; } -static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) +static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif, + struct ath11k_vif *tx_arvif) { struct ath11k *ar = arvif->ar; struct ath11k_base *ab = ar->ab; - struct ath11k_vif *tx_arvif = arvif; struct ieee80211_hw *hw = ar->hw; struct ieee80211_vif *vif = arvif->vif; struct ieee80211_mutable_offsets offs = {}; struct sk_buff *bcn; int ret; - if (vif->mbssid_tx_vif) { - tx_arvif = ath11k_vif_to_arvif(vif->mbssid_tx_vif); - if (tx_arvif != arvif) { - ar = tx_arvif->ar; - ab = ar->ab; - hw = ar->hw; - vif = tx_arvif->vif; - } + if (tx_arvif != arvif) { + ar = tx_arvif->ar; + ab = ar->ab; + hw = ar->hw; + vif = tx_arvif->vif; } bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); @@ -1534,10 +1621,12 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) return -EPERM; } - if (tx_arvif == arvif) - ath11k_mac_set_vif_params(tx_arvif, bcn); - else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) + if (tx_arvif == arvif) { + if (ath11k_mac_set_vif_params(tx_arvif, bcn)) + return -EINVAL; + } else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) { return -EINVAL; + } ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0); kfree_skb(bcn); @@ -1552,6 +1641,7 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) { struct ieee80211_vif *vif = arvif->vif; + struct ath11k_vif *tx_arvif; if (arvif->vdev_type != WMI_VDEV_TYPE_AP) return 0; @@ -1559,14 +1649,18 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) /* Target does not expect beacon templates for the already up * non-transmitting interfaces, and results in a crash if sent. */ - if (vif->mbssid_tx_vif && - arvif != ath11k_vif_to_arvif(vif->mbssid_tx_vif) && arvif->is_up) - return 0; + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (tx_arvif) { + if (arvif != tx_arvif && arvif->is_up) + return 0; - if (vif->bss_conf.ema_ap && vif->mbssid_tx_vif) - return ath11k_mac_setup_bcn_tmpl_ema(arvif); + if (vif->bss_conf.ema_ap) + return ath11k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif); + } else { + tx_arvif = arvif; + } - return ath11k_mac_setup_bcn_tmpl_mbssid(arvif); + return ath11k_mac_setup_bcn_tmpl_mbssid(arvif, tx_arvif); } void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) @@ -1579,7 +1673,7 @@ void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) if (vif->bss_conf.color_change_active && ieee80211_beacon_cntdwn_is_complete(vif, 0)) { arvif->bcca_zero_sent = true; - ieee80211_color_change_finish(vif); + ieee80211_color_change_finish(vif, 0); return; } @@ -1594,7 +1688,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif, struct ieee80211_bss_conf *info) { struct ath11k *ar = arvif->ar; - struct ath11k_vif *tx_arvif = NULL; + struct ath11k_vif *tx_arvif; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -1617,15 +1711,11 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif, return; } - arvif->tx_seq_no = 0x1000; - arvif->aid = 0; ether_addr_copy(arvif->bssid, info->bssid); - if (arvif->vif->mbssid_tx_vif) - tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); - + tx_arvif = ath11k_mac_get_tx_arvif(arvif); ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid, tx_arvif ? tx_arvif->bssid : NULL, @@ -2150,7 +2240,7 @@ static void ath11k_peer_assoc_h_vht(struct ath11k *ar, __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask); /* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default. - * VHT mcs rate 10 and 11 is not suppoerted in 11ac standard. + * VHT mcs rate 10 and 11 is not supported in 11ac standard. * so explicitly disable the VHT MCS rate 10 and 11 in 11ac mode. */ arg->tx_mcs_set &= ~IEEE80211_VHT_MCS_SUPPORT_0_11_MASK; @@ -3996,6 +4086,9 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; + if (ar->ab->hw_params.single_pdev_only) + arg->scan_f_filter_prb_req = 1; + if (req->ie_len) { arg->extraie.ptr = kmemdup(req->ie, req->ie_len, GFP_KERNEL); if (!arg->extraie.ptr) { @@ -4146,6 +4239,7 @@ static int ath11k_install_key(struct ath11k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: arg.key_cipher = WMI_CIPHER_AES_CCM; /* TODO: Re-check if flag is valid */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; @@ -4155,12 +4249,10 @@ static int ath11k_install_key(struct ath11k_vif *arvif, arg.key_txmic_len = 8; arg.key_rxmic_len = 8; break; - case WLAN_CIPHER_SUITE_CCMP_256: - arg.key_cipher = WMI_CIPHER_AES_CCM; - break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: arg.key_cipher = WMI_CIPHER_AES_GCM; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; default: ath11k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); @@ -4997,9 +5089,10 @@ static void ath11k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ieee80211_link_sta *link_sta, u32 changed) { + struct ieee80211_sta *sta = link_sta->sta; struct ath11k *ar = hw->priv; struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); @@ -5123,6 +5216,45 @@ exit: return ret; } +static int ath11k_mac_op_conf_tx_mu_edca(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k *ar = hw->priv; + struct wmi_wmm_params_arg *p; + int ret; + + switch (ac) { + case IEEE80211_AC_VO: + p = &arvif->muedca_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &arvif->muedca_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &arvif->muedca_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &arvif->muedca_params.ac_bk; + break; + default: + ath11k_warn(ar->ab, "error ac: %d", ac); + return -EINVAL; + } + + p->cwmin = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(3, 0)); + p->cwmax = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(7, 4)); + p->aifs = u8_get_bits(params->mu_edca_param_rec.aifsn, GENMASK(3, 0)); + p->txop = params->mu_edca_param_rec.mu_edca_timer; + + ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, + &arvif->muedca_params, + WMI_WMM_PARAM_TYPE_11AX_MU_EDCA); + return ret; +} + static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, u16 ac, @@ -5161,12 +5293,22 @@ static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, p->txop = params->txop; ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, - &arvif->wmm_params); + &arvif->wmm_params, + WMI_WMM_PARAM_TYPE_LEGACY); if (ret) { ath11k_warn(ar->ab, "failed to set wmm params: %d\n", ret); goto exit; } + if (params->mu_edca) { + ret = ath11k_mac_op_conf_tx_mu_edca(hw, vif, link_id, ac, + params); + if (ret) { + ath11k_warn(ar->ab, "failed to set mu_edca params: %d\n", ret); + goto exit; + } + } + ret = ath11k_conf_tx_uapsd(ar, vif, ac, params->uapsd); if (ret) @@ -5255,8 +5397,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) { nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); } @@ -5340,9 +5480,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) /* Enable Beamformee STS Field only if SU BF is enabled */ if (subfee) { - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; - nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; *vht_cap |= nsts; @@ -5820,7 +5957,10 @@ static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, { struct ath11k_base *ab = ar->ab; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); struct ieee80211_tx_info *info; + enum hal_encrypt_type enctype; + unsigned int mic_len; dma_addr_t paddr; int buf_id; int ret; @@ -5844,7 +5984,12 @@ static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, ieee80211_is_deauth(hdr->frame_control) || ieee80211_is_disassoc(hdr->frame_control)) && ieee80211_has_protected(hdr->frame_control)) { - skb_put(skb, IEEE80211_CCMP_MIC_LEN); + if (!(skb_cb->flags & ATH11K_SKB_CIPHER_SET)) + ath11k_warn(ab, "WMI management tx frame without ATH11K_SKB_CIPHER_SET"); + + enctype = ath11k_dp_tx_get_encrypt_type(skb_cb->cipher); + mic_len = ath11k_dp_rx_crypto_mic_len(ar, enctype); + skb_put(skb, mic_len); } } @@ -6025,7 +6170,7 @@ static int ath11k_mac_config_mon_status_default(struct ath11k *ar, bool enable) tlv_filter.rx_filter = ath11k_debugfs_rx_filter(ar); } - for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) { ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i, @@ -6195,10 +6340,11 @@ err: return ret; } -static void ath11k_mac_op_stop(struct ieee80211_hw *hw) +static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) { struct ath11k *ar = hw->priv; struct htt_ppdu_stats_info *ppdu_stats, *tmp; + struct scan_chan_list_params *params; int ret; ath11k_mac_drain_tx(ar); @@ -6214,6 +6360,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw) mutex_unlock(&ar->conf_mutex); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->update_11d_work); @@ -6223,10 +6370,19 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw) } spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { list_del(&ppdu_stats->list); kfree(ppdu_stats); } + + while ((params = list_first_entry_or_null(&ar->channel_update_queue, + struct scan_chan_list_params, + list))) { + list_del(¶ms->list); + kfree(params); + } + spin_unlock_bh(&ar->data_lock); rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); @@ -6241,23 +6397,20 @@ static int ath11k_mac_setup_vdev_params_mbssid(struct ath11k_vif *arvif, { struct ath11k *ar = arvif->ar; struct ath11k_vif *tx_arvif; - struct ieee80211_vif *tx_vif; *tx_vdev_id = 0; - tx_vif = arvif->vif->mbssid_tx_vif; - if (!tx_vif) { + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (!tx_arvif) { *flags = WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP; return 0; } - tx_arvif = ath11k_vif_to_arvif(tx_vif); - if (arvif->vif->bss_conf.nontransmitted) { - if (ar->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy) + if (ar->hw->wiphy != tx_arvif->ar->hw->wiphy) return -EINVAL; *flags = WMI_HOST_VDEV_FLAGS_NON_TRANSMIT_AP; - *tx_vdev_id = ath11k_vif_to_arvif(tx_vif)->vdev_id; + *tx_vdev_id = tx_arvif->vdev_id; } else if (tx_arvif == arvif) { *flags = WMI_HOST_VDEV_FLAGS_TRANSMIT_AP; } else { @@ -6509,6 +6662,16 @@ static int ath11k_mac_vdev_delete(struct ath11k *ar, struct ath11k_vif *arvif) return ret; } +static void ath11k_mac_bcn_tx_work(struct work_struct *work) +{ + struct ath11k_vif *arvif = container_of(work, struct ath11k_vif, + bcn_tx_work); + + mutex_lock(&arvif->ar->conf_mutex); + ath11k_mac_bcn_tx_event(arvif); + mutex_unlock(&arvif->ar->conf_mutex); +} + static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -6547,6 +6710,7 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, arvif->vif = vif; INIT_LIST_HEAD(&arvif->list); + INIT_WORK(&arvif->bcn_tx_work, ath11k_mac_bcn_tx_work); INIT_DELAYED_WORK(&arvif->connection_loss_work, ath11k_mac_vif_sta_connection_loss_work); @@ -6570,17 +6734,26 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: arvif->vdev_type = WMI_VDEV_TYPE_STA; + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT; break; case NL80211_IFTYPE_MESH_POINT: arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; fallthrough; case NL80211_IFTYPE_AP: arvif->vdev_type = WMI_VDEV_TYPE_AP; + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; break; case NL80211_IFTYPE_MONITOR: arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; ar->monitor_vdev_id = bit; break; + case NL80211_IFTYPE_P2P_DEVICE: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + break; + default: WARN_ON(1); break; @@ -6780,6 +6953,7 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, int i; cancel_delayed_work_sync(&arvif->connection_loss_work); + cancel_work_sync(&arvif->bcn_tx_work); mutex_lock(&ar->conf_mutex); @@ -6840,7 +7014,7 @@ err_vdev_del: /* Recalc txpower for remaining vdev */ ath11k_mac_txpower_recalc(ar); - /* TODO: recal traffic pause state based on the available vdevs */ + /* TODO: recalc traffic pause state based on the available vdevs */ mutex_unlock(&ar->conf_mutex); } @@ -7196,8 +7370,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, int n_vifs) { struct ath11k_base *ab = ar->ab; - struct ath11k_vif *arvif, *tx_arvif = NULL; - struct ieee80211_vif *mbssid_tx_vif; + struct ath11k_vif *arvif, *tx_arvif; int ret; int i; bool monitor_vif = false; @@ -7251,10 +7424,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n", ret); - mbssid_tx_vif = arvif->vif->mbssid_tx_vif; - if (mbssid_tx_vif) - tx_arvif = ath11k_vif_to_arvif(mbssid_tx_vif); - + tx_arvif = ath11k_mac_get_tx_arvif(arvif); ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid, tx_arvif ? tx_arvif->bssid : NULL, @@ -7415,32 +7585,6 @@ static int ath11k_mac_stop_vdev_early(struct ieee80211_hw *hw, return 0; } -static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt) -{ - switch (txpwr_intrprt) { - /* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield - * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of - * "IEEE Std 802.11ax 2021". - */ - case IEEE80211_TPE_LOCAL_EIRP: - case IEEE80211_TPE_REG_CLIENT_EIRP: - txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3; - txpwr_cnt = txpwr_cnt + 1; - break; - /* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield - * if Maximum Transmit Power Interpretation subfield is 1 or 3" of - * "IEEE Std 802.11ax 2021". - */ - case IEEE80211_TPE_LOCAL_EIRP_PSD: - case IEEE80211_TPE_REG_CLIENT_EIRP_PSD: - txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4; - txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1; - break; - } - - return txpwr_cnt; -} - static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def) { if (chan_def->chan->flags & IEEE80211_CHAN_PSD) { @@ -7596,7 +7740,7 @@ void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, struct ieee80211_channel *chan, *temp_chan; u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction; bool is_psd_power = false, is_tpe_present = false; - s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL], + s8 max_tx_power[ATH11K_NUM_PWR_LEVELS], psd_power, tx_power; s8 eirp_power = 0; u16 start_freq, center_freq; @@ -7609,7 +7753,8 @@ void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, is_tpe_present = true; num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels; } else { - num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def); + num_pwr_levels = + ath11k_mac_get_num_pwr_levels(&bss_conf->chanreq.oper); } for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) { @@ -7766,33 +7911,23 @@ static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar, struct ath11k_base *ab = ar->ab; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct ieee80211_tx_pwr_env *single_tpe; + struct ieee80211_parsed_tpe_eirp *non_psd = NULL; + struct ieee80211_parsed_tpe_psd *psd = NULL; enum wmi_reg_6ghz_client_type client_type; struct cur_regulatory_info *reg_info; + u8 local_tpe_count, reg_tpe_count; + bool use_local_tpe; int i; - u8 pwr_count, pwr_interpret, pwr_category; - u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0; - bool use_local_tpe, non_psd_set = false, psd_set = false; reg_info = &ab->reg_info_store[ar->pdev_idx]; client_type = reg_info->client_type; - for (i = 0; i < bss_conf->tx_pwr_env_num; i++) { - single_tpe = &bss_conf->tx_pwr_env[i]; - pwr_category = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_CATEGORY); - pwr_interpret = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_INTERPRET); - - if (pwr_category == client_type) { - if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP || - pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) - local_tpe_count++; - else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP || - pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) - reg_tpe_count++; - } - } + local_tpe_count = + bss_conf->tpe.max_local[client_type].valid + + bss_conf->tpe.psd_local[client_type].valid; + reg_tpe_count = + bss_conf->tpe.max_reg_client[client_type].valid + + bss_conf->tpe.psd_reg_client[client_type].valid; if (!reg_tpe_count && !local_tpe_count) { ath11k_warn(ab, @@ -7805,83 +7940,45 @@ static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar, use_local_tpe = false; } - for (i = 0; i < bss_conf->tx_pwr_env_num; i++) { - single_tpe = &bss_conf->tx_pwr_env[i]; - pwr_category = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_CATEGORY); - pwr_interpret = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_INTERPRET); - - if (pwr_category != client_type) - continue; - - /* get local transmit power envelope */ - if (use_local_tpe) { - if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) { - non_psd_index = i; - non_psd_set = true; - } else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) { - psd_index = i; - psd_set = true; - } - /* get regulatory transmit power envelope */ - } else { - if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) { - non_psd_index = i; - non_psd_set = true; - } else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) { - psd_index = i; - psd_set = true; - } - } + if (use_local_tpe) { + psd = &bss_conf->tpe.psd_local[client_type]; + if (!psd->valid) + psd = NULL; + non_psd = &bss_conf->tpe.max_local[client_type]; + if (!non_psd->valid) + non_psd = NULL; + } else { + psd = &bss_conf->tpe.psd_reg_client[client_type]; + if (!psd->valid) + psd = NULL; + non_psd = &bss_conf->tpe.max_reg_client[client_type]; + if (!non_psd->valid) + non_psd = NULL; } - if (non_psd_set && !psd_set) { - single_tpe = &bss_conf->tx_pwr_env[non_psd_index]; - pwr_count = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_COUNT); - pwr_interpret = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + if (non_psd && !psd) { arvif->reg_tpc_info.is_psd_power = false; arvif->reg_tpc_info.eirp_power = 0; - arvif->reg_tpc_info.num_pwr_levels = - ath11k_mac_get_tpe_count(pwr_interpret, pwr_count); + arvif->reg_tpc_info.num_pwr_levels = non_psd->count; for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { ath11k_dbg(ab, ATH11K_DBG_MAC, "non PSD power[%d] : %d\n", - i, single_tpe->tx_power[i]); - arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2; + i, non_psd->power[i]); + arvif->reg_tpc_info.tpe[i] = non_psd->power[i] / 2; } } - if (psd_set) { - single_tpe = &bss_conf->tx_pwr_env[psd_index]; - pwr_count = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_COUNT); - pwr_interpret = u8_get_bits(single_tpe->tx_power_info, - IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + if (psd) { arvif->reg_tpc_info.is_psd_power = true; + arvif->reg_tpc_info.num_pwr_levels = psd->count; - if (pwr_count == 0) { + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { ath11k_dbg(ab, ATH11K_DBG_MAC, - "TPE PSD power : %d\n", single_tpe->tx_power[0]); - arvif->reg_tpc_info.num_pwr_levels = - ath11k_mac_get_num_pwr_levels(&ctx->def); - - for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) - arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2; - } else { - arvif->reg_tpc_info.num_pwr_levels = - ath11k_mac_get_tpe_count(pwr_interpret, pwr_count); - - for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { - ath11k_dbg(ab, ATH11K_DBG_MAC, - "TPE PSD power[%d] : %d\n", - i, single_tpe->tx_power[i]); - arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2; - } + "TPE PSD power[%d] : %d\n", + i, psd->power[i]); + arvif->reg_tpc_info.tpe[i] = psd->power[i] / 2; } } } @@ -7896,8 +7993,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath11k_base *ab = ar->ab; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); int ret; - struct cur_regulatory_info *reg_info; - enum ieee80211_ap_reg_power power_type; mutex_lock(&ar->conf_mutex); @@ -7908,17 +8003,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, if (ath11k_wmi_supports_6ghz_cc_ext(ar) && ctx->def.chan->band == NL80211_BAND_6GHZ && arvif->vdev_type == WMI_VDEV_TYPE_STA) { - reg_info = &ab->reg_info_store[ar->pdev_idx]; - power_type = vif->bss_conf.power_type; - - ath11k_dbg(ab, ATH11K_DBG_MAC, "chanctx power type %d\n", power_type); - - if (power_type == IEEE80211_REG_UNSET_AP) { - ret = -EINVAL; - goto out; - } - - ath11k_reg_handle_chan_list(ab, reg_info, power_type); arvif->chanctx = *ctx; ath11k_mac_parse_tx_pwr_env(ar, vif, ctx); } @@ -8772,12 +8856,8 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, ieee80211_wake_queues(ar->hw); if (ar->ab->hw_params.current_cc_support && - ar->alpha2[0] != 0 && ar->alpha2[1] != 0) { - struct wmi_set_current_country_params set_current_param = {}; - - memcpy(&set_current_param.alpha2, ar->alpha2, 2); - ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); - } + ar->alpha2[0] != 0 && ar->alpha2[1] != 0) + ath11k_reg_set_cc(ar); if (ab->is_reset) { recovery_count = atomic_inc_return(&ab->recovery_count); @@ -8917,6 +8997,81 @@ static void ath11k_mac_put_chain_rssi(struct station_info *sinfo, } } +static void ath11k_mac_fw_stats_reset(struct ath11k *ar) +{ + spin_lock_bh(&ar->data_lock); + ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs); + ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs); + ar->fw_stats.num_vdev_recvd = 0; + ar->fw_stats.num_bcn_recvd = 0; + spin_unlock_bh(&ar->data_lock); +} + +int ath11k_mac_fw_stats_request(struct ath11k *ar, + struct stats_request_params *req_param) +{ + struct ath11k_base *ab = ar->ab; + unsigned long time_left; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_mac_fw_stats_reset(ar); + + reinit_completion(&ar->fw_stats_complete); + reinit_completion(&ar->fw_stats_done); + + ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); + + if (ret) { + ath11k_warn(ab, "could not request fw stats (%d)\n", + ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); + if (!time_left) + return -ETIMEDOUT; + + /* FW stats can get split when exceeding the stats data buffer limit. + * In that case, since there is no end marking for the back-to-back + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ + time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ); + if (!time_left) + return -ETIMEDOUT; + + return 0; +} + +static int ath11k_mac_get_fw_stats(struct ath11k *ar, u32 pdev_id, + u32 vdev_id, u32 stats_id) +{ + struct ath11k_base *ab = ar->ab; + struct stats_request_params req_param; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) + return -ENETDOWN; + + req_param.pdev_id = pdev_id; + req_param.vdev_id = vdev_id; + req_param.stats_id = stats_id; + + ret = ath11k_mac_fw_stats_request(ar, &req_param); + if (ret) + ath11k_warn(ab, "failed to request fw stats: %d\n", ret); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n", + pdev_id, vdev_id, stats_id); + + return ret; +} + static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -8951,11 +9106,12 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, ath11k_mac_put_chain_rssi(sinfo, arsta, "ppdu", false); + mutex_lock(&ar->conf_mutex); if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) && arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA && ar->ab->hw_params.supports_rssi_stats && - !ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0, - WMI_REQUEST_RSSI_PER_CHAIN_STAT)) { + !ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_RSSI_PER_CHAIN_STAT)) { ath11k_mac_put_chain_rssi(sinfo, arsta, "fw stats", true); } @@ -8963,9 +9119,10 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, if (!signal && arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA && ar->ab->hw_params.supports_rssi_stats && - !(ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0, - WMI_REQUEST_VDEV_STAT))) + !(ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_VDEV_STAT))) signal = arsta->rssi_beacon; + mutex_unlock(&ar->conf_mutex); ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "sta statistics db2dbm %u rssi comb %d rssi beacon %d\n", @@ -8976,8 +9133,11 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); } - sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi) + - ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi); + + if (!db2dbm) + sinfo->signal_avg += ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); } @@ -9012,7 +9172,6 @@ static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct inet6_ifaddr *ifa6; struct ifacaddr6 *ifaca6; - struct list_head *p; u32 count, scope; ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "op ipv6 changed\n"); @@ -9020,6 +9179,12 @@ static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, offload = &arvif->arp_ns_offload; count = 0; + /* The _ipv6_changed() is called with RCU lock already held in + * atomic_notifier_call_chain(), so we don't need to call + * rcu_read_lock() again here. But note that with CONFIG_PREEMPT_RT + * enabled, read_lock_bh() also calls rcu_read_lock(). This is OK + * because RCU read critical section is allowed to get nested. + */ read_lock_bh(&idev->lock); memset(offload->ipv6_addr, 0, sizeof(offload->ipv6_addr)); @@ -9027,11 +9192,10 @@ static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, memcpy(offload->mac_addr, vif->addr, ETH_ALEN); /* get unicast address */ - list_for_each(p, &idev->addr_list) { + list_for_each_entry(ifa6, &idev->addr_list, if_list) { if (count >= ATH11K_IPV6_MAX_COUNT) goto generate; - ifa6 = list_entry(p, struct inet6_ifaddr, if_list); if (ifa6->flags & IFA_F_DADFAILED) continue; scope = ipv6_addr_src_scope(&ifa6->addr); @@ -9050,7 +9214,8 @@ static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, } /* get anycast address */ - for (ifaca6 = idev->ac_list; ifaca6; ifaca6 = ifaca6->aca_next) { + for (ifaca6 = rcu_dereference(idev->ac_list); ifaca6; + ifaca6 = rcu_dereference(ifaca6->aca_next)) { if (count >= ATH11K_IPV6_MAX_COUNT) goto generate; @@ -9253,9 +9418,11 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, arg->dwell_time_passive = scan_time_msec; arg->max_scan_time = scan_time_msec; arg->scan_f_passive = 1; - arg->scan_f_filter_prb_req = 1; arg->burst_duration = duration; + if (!ar->ab->hw_params.single_pdev_only) + arg->scan_f_filter_prb_req = 1; + ret = ath11k_start_scan(ar, arg); if (ret) { ath11k_warn(ar->ab, "failed to start roc scan: %d\n", ret); @@ -9290,45 +9457,13 @@ exit: return ret; } -static int ath11k_fw_stats_request(struct ath11k *ar, - struct stats_request_params *req_param) -{ - struct ath11k_base *ab = ar->ab; - unsigned long time_left; - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - spin_lock_bh(&ar->data_lock); - ar->fw_stats_done = false; - ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs); - spin_unlock_bh(&ar->data_lock); - - reinit_completion(&ar->fw_stats_complete); - - ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); - if (ret) { - ath11k_warn(ab, "could not request fw stats (%d)\n", - ret); - return ret; - } - - time_left = wait_for_completion_timeout(&ar->fw_stats_complete, - 1 * HZ); - - if (!time_left) - return -ETIMEDOUT; - - return 0; -} - static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) { struct ath11k *ar = hw->priv; struct ath11k_base *ab = ar->ab; - struct stats_request_params req_param = {0}; struct ath11k_fw_stats_pdev *pdev; int ret; @@ -9340,9 +9475,6 @@ static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw, */ mutex_lock(&ar->conf_mutex); - if (ar->state != ATH11K_STATE_ON) - goto err_fallback; - /* Firmware doesn't provide Tx power during CAC hence no need to fetch * the stats. */ @@ -9351,10 +9483,8 @@ static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw, return -EAGAIN; } - req_param.pdev_id = ar->pdev->pdev_id; - req_param.stats_id = WMI_REQUEST_PDEV_STAT; - - ret = ath11k_fw_stats_request(ar, &req_param); + ret = ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_PDEV_STAT); if (ret) { ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret); goto err_fallback; @@ -9530,6 +9660,8 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + enum ieee80211_ap_reg_power power_type; + struct cur_regulatory_info *reg_info; struct ath11k_peer *peer; int ret = 0; @@ -9609,6 +9741,29 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", sta->addr, arvif->vdev_id, ret); } + + if (!ret && + ath11k_wmi_supports_6ghz_cc_ext(ar) && + arvif->vdev_type == WMI_VDEV_TYPE_STA && + arvif->chanctx.def.chan && + arvif->chanctx.def.chan->band == NL80211_BAND_6GHZ) { + reg_info = &ar->ab->reg_info_store[ar->pdev_idx]; + power_type = vif->bss_conf.power_type; + + if (power_type == IEEE80211_REG_UNSET_AP) { + ath11k_warn(ar->ab, "invalid power type %d\n", + power_type); + ret = -EINVAL; + } else { + ret = ath11k_reg_handle_chan_list(ar->ab, + reg_info, + power_type); + if (ret) + ath11k_warn(ar->ab, + "failed to handle chan list with power type %d\n", + power_type); + } + } } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { spin_lock_bh(&ar->ab->base_lock); @@ -9652,7 +9807,7 @@ static const struct ieee80211_ops ath11k_ops = { .sta_state = ath11k_mac_op_sta_state, .sta_set_4addr = ath11k_mac_op_sta_set_4addr, .sta_set_txpwr = ath11k_mac_op_sta_set_txpwr, - .sta_rc_update = ath11k_mac_op_sta_rc_update, + .link_sta_rc_update = ath11k_mac_op_sta_rc_update, .conf_tx = ath11k_mac_op_conf_tx, .set_antenna = ath11k_mac_op_set_antenna, .get_antenna = ath11k_mac_op_get_antenna, @@ -9856,13 +10011,24 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) struct ath11k_base *ab = ar->ab; struct ieee80211_iface_combination *combinations; struct ieee80211_iface_limit *limits; - int n_limits; + int n_limits, n_combos; + bool p2p; + + p2p = ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_P2P_DEVICE); + + if (ab->hw_params.support_dual_stations) + n_combos = 2; + else + n_combos = 1; - combinations = kzalloc(sizeof(*combinations), GFP_KERNEL); + combinations = kcalloc(n_combos, sizeof(*combinations), GFP_KERNEL); if (!combinations) return -ENOMEM; - n_limits = 2; + if (p2p) + n_limits = 3; + else + n_limits = 2; limits = kcalloc(n_limits, sizeof(*limits), GFP_KERNEL); if (!limits) { @@ -9870,49 +10036,47 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) return -ENOMEM; } + limits[0].max = 1; + limits[0].types |= BIT(NL80211_IFTYPE_STATION); + limits[1].max = 16; + limits[1].types |= BIT(NL80211_IFTYPE_AP); + if (IS_ENABLED(CONFIG_MAC80211_MESH) && + ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) + limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT); + + combinations[0].limits = limits; + combinations[0].n_limits = n_limits; + combinations[0].beacon_int_infra_match = true; + combinations[0].beacon_int_min_gcd = 100; + combinations[0].max_interfaces = 16; + combinations[0].num_different_channels = 1; + combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_80P80) | + BIT(NL80211_CHAN_WIDTH_160); + if (ab->hw_params.support_dual_stations) { limits[0].max = 2; - limits[0].types |= BIT(NL80211_IFTYPE_STATION); - - limits[1].max = 1; - limits[1].types |= BIT(NL80211_IFTYPE_AP); - if (IS_ENABLED(CONFIG_MAC80211_MESH) && - ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) - limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT); - - combinations[0].limits = limits; - combinations[0].n_limits = 2; - combinations[0].max_interfaces = ab->hw_params.num_vdevs; - combinations[0].num_different_channels = 2; - combinations[0].beacon_int_infra_match = true; - combinations[0].beacon_int_min_gcd = 100; - } else { - limits[0].max = 1; - limits[0].types |= BIT(NL80211_IFTYPE_STATION); - - limits[1].max = 16; - limits[1].types |= BIT(NL80211_IFTYPE_AP); - - if (IS_ENABLED(CONFIG_MAC80211_MESH) && - ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) - limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT); - - combinations[0].limits = limits; - combinations[0].n_limits = 2; - combinations[0].max_interfaces = 16; - combinations[0].num_different_channels = 1; - combinations[0].beacon_int_infra_match = true; - combinations[0].beacon_int_min_gcd = 100; - combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80) | - BIT(NL80211_CHAN_WIDTH_80P80) | - BIT(NL80211_CHAN_WIDTH_160); + + combinations[1].limits = limits; + combinations[1].n_limits = n_limits; + combinations[1].beacon_int_infra_match = true; + combinations[1].beacon_int_min_gcd = 100; + combinations[1].max_interfaces = ab->hw_params.num_vdevs; + combinations[1].num_different_channels = 2; + } + + if (p2p) { + limits[1].types |= BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + limits[2].max = 1; + limits[2].types |= BIT(NL80211_IFTYPE_P2P_DEVICE); } ar->hw->wiphy->iface_combinations = combinations; - ar->hw->wiphy->n_iface_combinations = 1; + ar->hw->wiphy->n_iface_combinations = n_combos; return 0; } @@ -9960,6 +10124,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { static void __ath11k_mac_unregister(struct ath11k *ar) { + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); ieee80211_unregister_hw(ar->hw); @@ -10027,6 +10192,7 @@ static int __ath11k_mac_register(struct ath11k *ar) if (ret) goto err; + wiphy_read_of_freq_limits(ar->hw->wiphy); ath11k_mac_setup_ht_vht_cap(ar, cap, &ht_cap); ath11k_mac_setup_he_cap(ar, cap); @@ -10213,11 +10379,8 @@ static int __ath11k_mac_register(struct ath11k *ar) } if (ab->hw_params.current_cc_support && ab->new_alpha2[0]) { - struct wmi_set_current_country_params set_current_param = {}; - - memcpy(&set_current_param.alpha2, ab->new_alpha2, 2); memcpy(&ar->alpha2, ab->new_alpha2, 2); - ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + ret = ath11k_reg_set_cc(ar); if (ret) ath11k_warn(ar->ab, "failed set cc code for mac register: %d\n", ret); @@ -10361,6 +10524,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab) init_completion(&ar->thermal.wmi_sync); INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); + INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work); + INIT_LIST_HEAD(&ar->channel_update_queue); INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index f5800fbecff8..5e61eea1bb03 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_MAC_H @@ -179,4 +179,6 @@ int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx); +int ath11k_mac_fw_stats_request(struct ath11k *ar, + struct stats_request_params *req_param); #endif diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index fb4ecf9a103e..acd76e9392d3 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/msi.h> @@ -19,6 +19,7 @@ #define MHI_TIMEOUT_DEFAULT_MS 20000 #define RDDM_DUMP_SIZE 0x420000 +#define MHI_CB_INVALID 0xff static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = { { @@ -158,9 +159,8 @@ void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_PCI, "mhistatus 0x%x\n", val); - /* Observed on QCA6390 that after SOC_GLOBAL_RESET, MHISTATUS - * has SYSERR bit set and thus need to set MHICTRL_RESET - * to clear SYSERR. + /* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set + * and thus need to set MHICTRL_RESET to clear SYSERR. */ ath11k_pcic_write32(ab, MHICTRL, MHICTRL_RESET_MASK); @@ -269,6 +269,7 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, enum mhi_callback cb) { struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev); + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ath11k_dbg(ab, ATH11K_DBG_BOOT, "notify status reason %s\n", ath11k_mhi_op_callback_to_str(cb)); @@ -279,12 +280,21 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, break; case MHI_CB_EE_RDDM: ath11k_warn(ab, "firmware crashed: MHI_CB_EE_RDDM\n"); + if (ab_pci->mhi_pre_cb == MHI_CB_EE_RDDM) { + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "do not queue again for consecutive RDDM event\n"); + break; + } + if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))) queue_work(ab->workqueue_aux, &ab->reset_work); + break; default: break; } + + ab_pci->mhi_pre_cb = cb; } static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl, @@ -388,6 +398,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) case ATH11K_HW_WCN6855_HW20: case ATH11K_HW_WCN6855_HW21: case ATH11K_HW_QCA2066_HW21: + case ATH11K_HW_QCA6698AQ_HW21: ath11k_mhi_config = &ath11k_mhi_config_qca6390; break; default: @@ -397,6 +408,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) goto free_controller; } + ab_pci->mhi_pre_cb = MHI_CB_INVALID; ret = mhi_register_controller(mhi_ctrl, ath11k_mhi_config); if (ret) { ath11k_err(ab, "failed to register to mhi bus, err = %d\n", ret); @@ -442,9 +454,17 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci) return 0; } -void ath11k_mhi_stop(struct ath11k_pci *ab_pci) +void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend) { - mhi_power_down(ab_pci->mhi_ctrl, true); + /* During suspend we need to use mhi_power_down_keep_dev() + * workaround, otherwise ath11k_core_resume() will timeout + * during resume. + */ + if (is_suspend) + mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true); + else + mhi_power_down(ab_pci->mhi_ctrl, true); + mhi_unprepare_after_power_down(ab_pci->mhi_ctrl); } @@ -479,3 +499,8 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci) return 0; } + +void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic) +{ + mhi_download_rddm_image(mhi_ctrl, in_panic); +} diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h index f81fba2644a4..5c5c2b03c81f 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.h +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH11K_MHI_H #define _ATH11K_MHI_H @@ -18,7 +18,7 @@ #define MHICTRL_RESET_MASK 0x2 int ath11k_mhi_start(struct ath11k_pci *ar_pci); -void ath11k_mhi_stop(struct ath11k_pci *ar_pci); +void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend); int ath11k_mhi_register(struct ath11k_pci *ar_pci); void ath11k_mhi_unregister(struct ath11k_pci *ar_pci); void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab); @@ -26,5 +26,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab); int ath11k_mhi_suspend(struct ath11k_pci *ar_pci); int ath11k_mhi_resume(struct ath11k_pci *ar_pci); +void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic); #endif diff --git a/drivers/net/wireless/ath/ath11k/p2p.c b/drivers/net/wireless/ath/ath11k/p2p.c new file mode 100644 index 000000000000..01e14523f1fe --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/p2p.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "core.h" +#include "wmi.h" +#include "mac.h" +#include "p2p.h" + +static void ath11k_p2p_noa_ie_fill(u8 *data, size_t len, + const struct ath11k_wmi_p2p_noa_info *noa) +{ + struct ieee80211_p2p_noa_attr *noa_attr; + u8 noa_descriptors, ctwindow; + bool oppps; + __le16 *noa_attr_len; + u16 attr_len; + int i; + + ctwindow = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_CTWIN_TU); + oppps = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS); + noa_descriptors = u32_get_bits(noa->noa_attr, + WMI_P2P_NOA_INFO_DESC_NUM); + + /* P2P IE */ + data[0] = WLAN_EID_VENDOR_SPECIFIC; + data[1] = len - 2; + data[2] = (WLAN_OUI_WFA >> 16) & 0xff; + data[3] = (WLAN_OUI_WFA >> 8) & 0xff; + data[4] = (WLAN_OUI_WFA >> 0) & 0xff; + data[5] = WLAN_OUI_TYPE_WFA_P2P; + + /* NOA ATTR */ + data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE; + noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */ + noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9]; + + noa_attr->index = u32_get_bits(noa->noa_attr, + WMI_P2P_NOA_INFO_INDEX); + noa_attr->oppps_ctwindow = ctwindow; + if (oppps) + noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; + + for (i = 0; i < noa_descriptors; i++) { + noa_attr->desc[i].count = noa->descriptors[i].type_count; + noa_attr->desc[i].duration = + cpu_to_le32(noa->descriptors[i].duration); + noa_attr->desc[i].interval = + cpu_to_le32(noa->descriptors[i].interval); + noa_attr->desc[i].start_time = + cpu_to_le32(noa->descriptors[i].start_time); + } + + attr_len = 2; /* index + oppps_ctwindow */ + attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); + *noa_attr_len = __cpu_to_le16(attr_len); +} + +static size_t +ath11k_p2p_noa_ie_len_compute(const struct ath11k_wmi_p2p_noa_info *noa) +{ + size_t len = 0; + u8 noa_descriptors = u32_get_bits(noa->noa_attr, + WMI_P2P_NOA_INFO_DESC_NUM); + + if (!(noa_descriptors) && + !(u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS))) + return 0; + + len += 1 + 1 + 4; /* EID + len + OUI */ + len += 1 + 2; /* noa attr + attr len */ + len += 1 + 1; /* index + oppps_ctwindow */ + len += noa_descriptors * + sizeof(struct ieee80211_p2p_noa_desc); + + return len; +} + +static void ath11k_p2p_noa_ie_assign(struct ath11k_vif *arvif, void *ie, + size_t len) +{ + struct ath11k *ar = arvif->ar; + + lockdep_assert_held(&ar->data_lock); + + kfree(arvif->u.ap.noa_data); + + arvif->u.ap.noa_data = ie; + arvif->u.ap.noa_len = len; +} + +static void __ath11k_p2p_noa_update(struct ath11k_vif *arvif, + const struct ath11k_wmi_p2p_noa_info *noa) +{ + struct ath11k *ar = arvif->ar; + void *ie; + size_t len; + + lockdep_assert_held(&ar->data_lock); + + ath11k_p2p_noa_ie_assign(arvif, NULL, 0); + + len = ath11k_p2p_noa_ie_len_compute(noa); + if (!len) + return; + + ie = kmalloc(len, GFP_ATOMIC); + if (!ie) + return; + + ath11k_p2p_noa_ie_fill(ie, len, noa); + ath11k_p2p_noa_ie_assign(arvif, ie, len); } + +void ath11k_p2p_noa_update(struct ath11k_vif *arvif, + const struct ath11k_wmi_p2p_noa_info *noa) +{ + struct ath11k *ar = arvif->ar; + + spin_lock_bh(&ar->data_lock); + __ath11k_p2p_noa_update(arvif, noa); + spin_unlock_bh(&ar->data_lock); +} + +static void ath11k_p2p_noa_update_vdev_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_p2p_noa_arg *arg = data; + + if (arvif->vdev_id != arg->vdev_id) + return; + + ath11k_p2p_noa_update(arvif, arg->noa); +} + +void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id, + const struct ath11k_wmi_p2p_noa_info *noa) +{ + struct ath11k_p2p_noa_arg arg = { + .vdev_id = vdev_id, + .noa = noa, + }; + + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath11k_p2p_noa_update_vdev_iter, + &arg); +} diff --git a/drivers/net/wireless/ath/ath11k/p2p.h b/drivers/net/wireless/ath/ath11k/p2p.h new file mode 100644 index 000000000000..d907940a9b09 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/p2p.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef ATH11K_P2P_H +#define ATH11K_P2P_H + +#include "wmi.h" + +struct ath11k_wmi_p2p_noa_info; + +struct ath11k_p2p_noa_arg { + u32 vdev_id; + const struct ath11k_wmi_p2p_noa_info *noa; +}; + +void ath11k_p2p_noa_update(struct ath11k_vif *arvif, + const struct ath11k_wmi_p2p_noa_info *noa); +void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id, + const struct ath11k_wmi_p2p_noa_info *noa); +#endif diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index be9d2c69cc41..78444f8ea153 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1,13 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> #include <linux/msi.h> #include <linux/pci.h> #include <linux/of.h> +#include <linux/time.h> +#include <linux/vmalloc.h> #include "pci.h" #include "core.h" @@ -610,6 +612,187 @@ static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) PCI_EXP_LNKCTL_ASPMC); } +#ifdef CONFIG_DEV_COREDUMP +static int ath11k_pci_coredump_calculate_size(struct ath11k_base *ab, u32 *dump_seg_sz) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct ath11k_tlv_dump_data *dump_tlv; + enum ath11k_fw_crash_dump_type mem_type; + u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0; + struct ath11k_dump_file_data *file_data; + int i; + + rddm_img = mhi_ctrl->rddm_image; + if (!rddm_img) { + ath11k_err(ab, "No RDDM dump found\n"); + return 0; + } + + fw_img = mhi_ctrl->fbc_image; + + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + paging_tlv_sz += fw_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz; + + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + rddm_tlv_sz += rddm_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type(ab->qmi.target_mem[i].type); + + if (mem_type == FW_CRASH_DUMP_NONE) + continue; + + if (mem_type == FW_CRASH_DUMP_TYPE_MAX) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "target mem region type %d not supported", + ab->qmi.target_mem[i].type); + continue; + } + + if (!ab->qmi.target_mem[i].anyaddr) + continue; + + dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size; + } + + for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) { + if (!dump_seg_sz[i]) + continue; + + len += sizeof(*dump_tlv) + dump_seg_sz[i]; + } + + if (len) + len += sizeof(*file_data); + + return len; +} + +static void ath11k_pci_coredump_download(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct timespec64 timestamp; + int i, len, mem_idx; + enum ath11k_fw_crash_dump_type mem_type; + struct ath11k_dump_file_data *file_data; + struct ath11k_tlv_dump_data *dump_tlv; + size_t hdr_len = sizeof(*file_data); + void *buf; + u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = { 0 }; + + ath11k_mhi_coredump(mhi_ctrl, false); + + len = ath11k_pci_coredump_calculate_size(ab, dump_seg_sz); + if (!len) { + ath11k_warn(ab, "No crash dump data found for devcoredump"); + return; + } + + rddm_img = mhi_ctrl->rddm_image; + fw_img = mhi_ctrl->fbc_image; + + /* dev_coredumpv() requires vmalloc data */ + buf = vzalloc(len); + if (!buf) + return; + + ab->dump_data = buf; + ab->ath11k_coredump_len = len; + file_data = ab->dump_data; + strscpy(file_data->df_magic, "ATH11K-FW-DUMP", sizeof(file_data->df_magic)); + file_data->len = cpu_to_le32(len); + file_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_V2); + file_data->chip_id = cpu_to_le32(ab_pci->dev_id); + file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id); + file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus)); + guid_gen(&file_data->guid); + ktime_get_real_ts64(×tamp); + file_data->tv_sec = cpu_to_le64(timestamp.tv_sec); + file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec); + buf += hdr_len; + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf, + fw_img->mhi_buf[i].len); + buf += fw_img->mhi_buf[i].len; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf, + rddm_img->mhi_buf[i].len); + buf += rddm_img->mhi_buf[i].len; + } + + mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA; + for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) { + if (mem_idx == FW_CRASH_DUMP_NONE) + continue; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type + (ab->qmi.target_mem[i].type); + + if (mem_type != mem_idx) + continue; + + if (!ab->qmi.target_mem[i].anyaddr) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "Skipping mem region type %d", + ab->qmi.target_mem[i].type); + continue; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(mem_idx); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]); + buf += COREDUMP_TLV_HDR_SIZE; + + memcpy_fromio(buf, ab->qmi.target_mem[i].iaddr, + ab->qmi.target_mem[i].size); + + buf += ab->qmi.target_mem[i].size; + } + } + + queue_work(ab->workqueue, &ab->dump_work); +} +#endif + static int ath11k_pci_power_up(struct ath11k_base *ab) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); @@ -638,7 +821,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab) return 0; } -static void ath11k_pci_power_down(struct ath11k_base *ab) +static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); @@ -649,7 +832,7 @@ static void ath11k_pci_power_down(struct ath11k_base *ab) ath11k_pci_msi_disable(ab_pci); - ath11k_mhi_stop(ab_pci); + ath11k_mhi_stop(ab_pci, is_suspend); clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); ath11k_pci_sw_reset(ab_pci->ab, false); } @@ -713,6 +896,9 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = { .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, +#ifdef CONFIG_DEV_COREDUMP + .coredump_download = ath11k_pci_coredump_download, +#endif }; static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor) @@ -735,7 +921,7 @@ static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab_pci->ab->dev_flags)) return 0; - return irq_set_affinity_hint(ab_pci->pdev->irq, m); + return irq_set_affinity_and_hint(ab_pci->pdev->irq, m); } static int ath11k_pci_probe(struct pci_dev *pdev, @@ -743,7 +929,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev, { struct ath11k_base *ab; struct ath11k_pci *ab_pci; - u32 soc_hw_version_major, soc_hw_version_minor, addr; + u32 soc_hw_version_major, soc_hw_version_minor; int ret; u32 sub_version; @@ -769,8 +955,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev, * from DT. If memory is reserved from DT for FW, ath11k driver need not * allocate memory. */ - ret = of_property_read_u32(ab->dev->of_node, "memory-region", &addr); - if (!ret) + if (of_property_present(ab->dev->of_node, "memory-region")) set_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags); ret = ath11k_pci_claim(ab_pci, pdev); @@ -846,6 +1031,9 @@ static int ath11k_pci_probe(struct pci_dev *pdev, case 0x1019D0E1: ab->hw_rev = ATH11K_HW_QCA2066_HW21; break; + case 0x001e60e1: + ab->hw_rev = ATH11K_HW_QCA6698AQ_HW21; + break; default: ab->hw_rev = ATH11K_HW_WCN6855_HW21; } @@ -936,6 +1124,8 @@ unsupported_wcn6855_soc: return 0; err_free_irq: + /* __free_irq() expects the caller to have cleared the affinity hint */ + ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ath11k_pcic_free_irq(ab); err_ce_free: @@ -970,17 +1160,21 @@ static void ath11k_pci_remove(struct pci_dev *pdev) ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { - ath11k_pci_power_down(ab); + ath11k_pci_power_down(ab, false); ath11k_debugfs_soc_destroy(ab); ath11k_qmi_deinit_service(ab); + ath11k_core_pm_notifier_unregister(ab); goto qmi_fail; } set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->reset_work); + cancel_work_sync(&ab->dump_work); ath11k_core_deinit(ab); qmi_fail: + ath11k_fw_destroy(ab); ath11k_mhi_unregister(ab_pci); ath11k_pcic_free_irq(ab); @@ -998,7 +1192,7 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev) struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); - ath11k_pci_power_down(ab); + ath11k_pci_power_down(ab, false); } static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev) @@ -1035,9 +1229,39 @@ static __maybe_unused int ath11k_pci_pm_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops, - ath11k_pci_pm_suspend, - ath11k_pci_pm_resume); +static __maybe_unused int ath11k_pci_pm_suspend_late(struct device *dev) +{ + struct ath11k_base *ab = dev_get_drvdata(dev); + int ret; + + ret = ath11k_core_suspend_late(ab); + if (ret) + ath11k_warn(ab, "failed to late suspend core: %d\n", ret); + + /* Similar to ath11k_pci_pm_suspend(), we return success here + * even error happens, to allow system suspend/hibernation survive. + */ + return 0; +} + +static __maybe_unused int ath11k_pci_pm_resume_early(struct device *dev) +{ + struct ath11k_base *ab = dev_get_drvdata(dev); + int ret; + + ret = ath11k_core_resume_early(ab); + if (ret) + ath11k_warn(ab, "failed to early resume core: %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops __maybe_unused ath11k_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend, + ath11k_pci_pm_resume) + SET_LATE_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend_late, + ath11k_pci_pm_resume_early) +}; static struct pci_driver ath11k_pci_driver = { .name = "ath11k_pci", diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h index 6be73333d90b..c33c7865145c 100644 --- a/drivers/net/wireless/ath/ath11k/pci.h +++ b/drivers/net/wireless/ath/ath11k/pci.h @@ -64,6 +64,7 @@ struct ath11k_pci { char amss_path[100]; struct mhi_controller *mhi_ctrl; const struct ath11k_msi_config *msi_config; + enum mhi_callback mhi_pre_cb; u32 register_window; /* protects register_window above */ diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index add4db4c50bc..3fe77310c71f 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -126,6 +126,17 @@ static const struct ath11k_msi_config ath11k_msi_config[] = { }, .hw_rev = ATH11K_HW_QCA2066_HW21, }, + { + .total_vectors = 32, + .total_users = 4, + .users = (struct ath11k_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, + .hw_rev = ATH11K_HW_QCA6698AQ_HW21, + }, }; int ath11k_pcic_init_msi_config(struct ath11k_base *ab) @@ -316,6 +327,7 @@ static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab) free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); netif_napi_del(&irq_grp->napi); + free_netdev(irq_grp->napi_ndev); } } @@ -558,8 +570,9 @@ ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector) static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) { - int i, j, ret, num_vectors = 0; + int i, j, n, ret, num_vectors = 0; u32 user_base_data = 0, base_vector = 0; + struct ath11k_ext_irq_grp *irq_grp; unsigned long irq_flags; ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, @@ -573,13 +586,18 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) irq_flags |= IRQF_NOBALANCING; for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { - struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + irq_grp = &ab->ext_irq_grp[i]; u32 num_irq = 0; irq_grp->ab = ab; irq_grp->grp_id = i; - init_dummy_netdev(&irq_grp->napi_ndev); - netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + irq_grp->napi_ndev = alloc_netdev_dummy(0); + if (!irq_grp->napi_ndev) { + ret = -ENOMEM; + goto fail_allocate; + } + + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath11k_pcic_ext_grp_napi_poll); if (ab->hw_params.ring_mask->tx[i] || @@ -601,8 +619,10 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) int vector = (i % num_vectors) + base_vector; int irq = ath11k_pcic_get_msi_irq(ab, vector); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto fail_irq; + } ab->irq_num[irq_idx] = irq; @@ -615,6 +635,10 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) if (ret) { ath11k_err(ab, "failed request irq %d: %d\n", vector, ret); + for (n = 0; n <= i; n++) { + irq_grp = &ab->ext_irq_grp[n]; + free_netdev(irq_grp->napi_ndev); + } return ret; } } @@ -622,6 +646,15 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) } return 0; +fail_irq: + /* i ->napi_ndev was properly allocated. Free it also */ + i += 1; +fail_allocate: + for (n = 0; n < i; n++) { + irq_grp = &ab->ext_irq_grp[n]; + free_netdev(irq_grp->napi_ndev); + } + return ret; } int ath11k_pcic_config_irq(struct ath11k_base *ab) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 5006f81f779b..2782f4723e41 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.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-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/elf.h> @@ -1704,7 +1704,9 @@ static const struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { }, }; -static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath11k_qmi_host_cap_send(struct ath11k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req; struct qmi_wlanfw_host_cap_resp_msg_v01 resp; @@ -1955,13 +1957,15 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) int i; for (i = 0; i < ab->qmi.mem_seg_count; i++) { - if ((ab->hw_params.fixed_mem_region || - test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && - ab->qmi.target_mem[i].iaddr) - iounmap(ab->qmi.target_mem[i].iaddr); + if (!ab->qmi.target_mem[i].anyaddr) + continue; - if (!ab->qmi.target_mem[i].vaddr) + if (ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { + iounmap(ab->qmi.target_mem[i].iaddr); + ab->qmi.target_mem[i].iaddr = NULL; continue; + } dma_free_coherent(ab->dev, ab->qmi.target_mem[i].prev_size, @@ -1989,6 +1993,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) chunk->prev_size == chunk->size) continue; + if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) { + ath11k_dbg(ab, ATH11K_DBG_QMI, + "size/type mismatch (current %d %u) (prev %d %u), try later with small size\n", + chunk->size, chunk->type, + chunk->prev_size, chunk->prev_type); + ab->qmi.target_mem_delayed = true; + return 0; + } + /* cannot reuse the existing chunk */ dma_free_coherent(ab->dev, chunk->prev_size, chunk->vaddr, chunk->paddr); @@ -2068,7 +2081,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) break; case BDF_MEM_REGION_TYPE: ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; @@ -2091,10 +2104,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) } else { ab->qmi.target_mem[idx].paddr = ATH11K_QMI_CALDB_ADDRESS; + ab->qmi.target_mem[idx].iaddr = NULL; } } else { ab->qmi.target_mem[idx].paddr = 0; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; } ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; @@ -2180,6 +2194,9 @@ static int ath11k_qmi_request_device_info(struct ath11k_base *ab) ab->mem = bar_addr_va; ab->mem_len = resp.bar_size; + if (!ab->hw_params.ce_remap) + ab->mem_ce = ab->mem; + return 0; out: return ret; @@ -2293,7 +2310,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, struct qmi_txn txn; const u8 *temp = data; void __iomem *bdf_addr = NULL; - int ret; + int ret = 0; u32 remaining = len; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -2567,7 +2584,9 @@ static void ath11k_qmi_m3_free(struct ath11k_base *ab) m3_mem->size = 0; } -static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; struct qmi_wlanfw_m3_info_req_msg_v01 req; @@ -2859,7 +2878,7 @@ int ath11k_qmi_firmware_start(struct ath11k_base *ab, int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) { - int timeout; + long time_left; if (!ath11k_core_coldboot_cal_support(ab) || ab->hw_params.cbcal_restart_fw == 0) @@ -2867,17 +2886,17 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "wait for cold boot done\n"); - timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, - (ab->qmi.cal_done == 1), - ATH11K_COLD_BOOT_FW_RESET_DELAY); + time_left = wait_event_timeout(ab->qmi.cold_boot_waitq, + (ab->qmi.cal_done == 1), + ATH11K_COLD_BOOT_FW_RESET_DELAY); - if (timeout <= 0) { + if (time_left <= 0) { ath11k_warn(ab, "Coldboot Calibration timed out\n"); return -ETIMEDOUT; } /* reset the firmware */ - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); ath11k_dbg(ab, ATH11K_DBG_QMI, "exit wait for cold boot done\n"); return 0; @@ -2886,7 +2905,7 @@ EXPORT_SYMBOL(ath11k_qmi_fwreset_from_cold_boot); static int ath11k_qmi_process_coldboot_calibration(struct ath11k_base *ab) { - int timeout; + long time_left; int ret; ret = ath11k_qmi_wlanfw_mode_send(ab, ATH11K_FIRMWARE_MODE_COLD_BOOT); @@ -2897,10 +2916,10 @@ static int ath11k_qmi_process_coldboot_calibration(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "Coldboot calibration wait started\n"); - timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, - (ab->qmi.cal_done == 1), - ATH11K_COLD_BOOT_FW_RESET_DELAY); - if (timeout <= 0) { + time_left = wait_event_timeout(ab->qmi.cold_boot_waitq, + (ab->qmi.cal_done == 1), + ATH11K_COLD_BOOT_FW_RESET_DELAY); + if (time_left <= 0) { ath11k_warn(ab, "coldboot calibration timed out\n"); return 0; } diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 7e06d100af57..7968ab122b65 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_QMI_H @@ -102,8 +102,11 @@ struct target_mem_chunk { u32 prev_size; u32 prev_type; dma_addr_t paddr; - u32 *vaddr; - void __iomem *iaddr; + union { + u32 *vaddr; + void __iomem *iaddr; + void *anyaddr; + }; }; struct target_info { @@ -154,6 +157,7 @@ struct ath11k_qmi { #define BDF_MEM_REGION_TYPE 0x2 #define M3_DUMP_REGION_TYPE 0x3 #define CALDB_MEM_REGION_TYPE 0x4 +#define PAGEABLE_MEM_REGION_TYPE 0x9 struct qmi_wlanfw_host_cap_req_msg_v01 { u8 num_clients_valid; diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 737fcd450d4b..d62a2014315a 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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/rtnetlink.h> @@ -49,13 +49,25 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wmi_init_country_params init_country_param; - struct wmi_set_current_country_params set_current_param = {}; struct ath11k *ar = hw->priv; int ret; ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "driver initiated regd update\n"); + if (ar->state != ATH11K_STATE_ON) + return; + + ret = ath11k_reg_update_chan_list(ar, true); + if (ret) + ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret); + + return; + } + /* Currently supporting only General User Hints. Cell base user * hints to be handled later. * Hints from other sources like Core, Beacons are not expected for @@ -83,9 +95,8 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) * reg info */ if (ar->ab->hw_params.current_cc_support) { - memcpy(&set_current_param.alpha2, request->alpha2, 2); - memcpy(&ar->alpha2, &set_current_param.alpha2, 2); - ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + memcpy(&ar->alpha2, request->alpha2, 2); + ret = ath11k_reg_set_cc(ar); if (ret) ath11k_warn(ar->ab, "failed set current country code: %d\n", ret); @@ -113,32 +124,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) struct channel_param *ch; enum nl80211_band band; int num_channels = 0; - int i, ret, left; - - if (wait && ar->state_11d != ATH11K_11D_IDLE) { - left = wait_for_completion_timeout(&ar->completed_11d_scan, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) { - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive 11d scan complete: timed out\n"); - ar->state_11d = ATH11K_11D_IDLE; - } - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "11d scan wait left time %d\n", left); - } - - if (wait && - (ar->scan.state == ATH11K_SCAN_STARTING || - ar->scan.state == ATH11K_SCAN_RUNNING)) { - left = wait_for_completion_timeout(&ar->scan.completed, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive hw scan complete: timed out\n"); - - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "hw scan wait left time %d\n", left); - } + int i, ret = 0; if (ar->state == ATH11K_STATE_RESTARTING) return 0; @@ -220,6 +206,16 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) } } + if (wait) { + spin_lock_bh(&ar->data_lock); + list_add_tail(¶ms->list, &ar->channel_update_queue); + spin_unlock_bh(&ar->data_lock); + + queue_work(ar->ab->workqueue, &ar->channel_update_work); + + return 0; + } + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); kfree(params); @@ -295,12 +291,6 @@ int ath11k_regd_update(struct ath11k *ar) if (ret) goto err; - if (ar->state == ATH11K_STATE_ON) { - ret = ath11k_reg_update_chan_list(ar, true); - if (ret) - goto err; - } - return 0; err: ath11k_warn(ab, "failed to perform regd update : %d\n", ret); @@ -806,6 +796,54 @@ ret: return new_regd; } +void ath11k_regd_update_chan_list_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + channel_update_work); + struct scan_chan_list_params *params; + struct list_head local_update_list; + int left; + + INIT_LIST_HEAD(&local_update_list); + + spin_lock_bh(&ar->data_lock); + list_splice_tail_init(&ar->channel_update_queue, &local_update_list); + spin_unlock_bh(&ar->data_lock); + + while ((params = list_first_entry_or_null(&local_update_list, + struct scan_chan_list_params, + list))) { + if (ar->state_11d != ATH11K_11D_IDLE) { + left = wait_for_completion_timeout(&ar->completed_11d_scan, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive 11d scan complete: timed out\n"); + ar->state_11d = ATH11K_11D_IDLE; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg 11d scan wait left time %d\n", left); + } + + if ((ar->scan.state == ATH11K_SCAN_STARTING || + ar->scan.state == ATH11K_SCAN_RUNNING)) { + left = wait_for_completion_timeout(&ar->scan.completed, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive hw scan complete: timed out\n"); + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg hw scan wait left time %d\n", left); + } + + ath11k_wmi_send_scan_chan_list_cmd(ar, params); + list_del(¶ms->list); + kfree(params); + } +} + static bool ath11k_reg_is_world_alpha(char *alpha) { if (alpha[0] == '0' && alpha[1] == '0') @@ -878,7 +916,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab, ath11k_reg_reset_info(reg_info); if (ab->hw_params.single_pdev_only && - pdev_idx < ab->hw_params.num_rxmda_per_pdev) + pdev_idx < ab->hw_params.num_rxdma_per_pdev) return 0; goto fallback; } @@ -979,6 +1017,7 @@ void ath11k_regd_update_work(struct work_struct *work) void ath11k_reg_init(struct ath11k *ar) { ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER; ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; } @@ -1017,3 +1056,11 @@ void ath11k_reg_free(struct ath11k_base *ab) kfree(ab->new_regd[i]); } } + +int ath11k_reg_set_cc(struct ath11k *ar) +{ + struct wmi_set_current_country_params set_current_param = {}; + + memcpy(&set_current_param.alpha2, ar->alpha2, 2); + return ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); +} diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 64edb794260a..72b483594015 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_REG_H @@ -33,6 +33,7 @@ void ath11k_reg_init(struct ath11k *ar); void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info); void ath11k_reg_free(struct ath11k_base *ab); void ath11k_regd_update_work(struct work_struct *work); +void ath11k_regd_update_chan_list_work(struct work_struct *work); struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, struct cur_regulatory_info *reg_info, bool intersect, @@ -45,5 +46,5 @@ ath11k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type); int ath11k_reg_handle_chan_list(struct ath11k_base *ab, struct cur_regulatory_info *reg_info, enum ieee80211_ap_reg_power power_type); - +int ath11k_reg_set_cc(struct ath11k *ar); #endif diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index 302d66092b97..a9751ea2a0b7 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "testmode.h" @@ -10,18 +10,18 @@ #include "wmi.h" #include "hw.h" #include "core.h" -#include "testmode_i.h" +#include "../testmode_i.h" #define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) #define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) -static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { - [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, - .len = ATH11K_TM_DATA_MAX_LEN }, - [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +static const struct nla_policy ath11k_tm_policy[ATH_TM_ATTR_MAX + 1] = { + [ATH_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH_TM_DATA_MAX_LEN }, + [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, }; static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab) @@ -73,9 +73,9 @@ static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id, goto out; } - if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) || - nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || - nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) { + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) { ath11k_warn(ab, "failed to populate testmode unsegmented event\n"); kfree_skb(nl_skb); goto out; @@ -107,7 +107,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, u32 pdev_id; ath11k_dbg(ab, ATH11K_DBG_TESTMODE, - "event wmi cmd_id %d ftm event msg %pK datalen %d\n", + "event wmi cmd_id %d ftm event msg %p datalen %d\n", cmd_id, ftm_msg, length); ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length); pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id); @@ -140,7 +140,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, data_pos = ab->testmode.data_pos; - if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) { + if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) { ath11k_warn(ab, "Invalid ftm event length at %d: %d\n", data_pos, datalen); ret = -EINVAL; @@ -172,10 +172,10 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, goto out; } - if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, - ATH11K_TM_CMD_WMI_FTM) || - nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || - nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos, + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, + ATH_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos, &ab->testmode.eventdata[0])) { ath11k_warn(ab, "failed to populate segmented testmode event"); kfree_skb(nl_skb); @@ -235,23 +235,23 @@ static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "cmd get version_major %d version_minor %d\n", - ATH11K_TESTMODE_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MINOR); + ATH_TESTMODE_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MINOR); skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, nla_total_size(sizeof(u32))); if (!skb) return -ENOMEM; - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MAJOR); + ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MAJOR); if (ret) { kfree_skb(skb); return ret; } - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, - ATH11K_TESTMODE_VERSION_MINOR); + ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR, + ATH_TESTMODE_VERSION_MINOR); if (ret) { kfree_skb(skb); return ret; @@ -277,7 +277,7 @@ static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[]) goto err; } - ar->ab->testmode.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH, + ar->ab->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL); if (!ar->ab->testmode.eventdata) { ret = -ENOMEM; @@ -310,25 +310,25 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[], mutex_lock(&ar->conf_mutex); - if (!tb[ATH11K_TM_ATTR_DATA]) { + if (!tb[ATH_TM_ATTR_DATA]) { ret = -EINVAL; goto out; } - if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) { + if (!tb[ATH_TM_ATTR_WMI_CMDID]) { ret = -EINVAL; goto out; } - buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); if (!buf_len) { ath11k_warn(ar->ab, "No data present in testmode wmi command\n"); ret = -EINVAL; goto out; } - cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); + cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]); /* Make sure that the buffer length is long enough to * hold TLV and pdev/vdev id. @@ -409,13 +409,13 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) goto out; } - if (!tb[ATH11K_TM_ATTR_DATA]) { + if (!tb[ATH_TM_ATTR_DATA]) { ret = -EINVAL; goto out; } - buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); cmd_id = WMI_PDEV_UTF_CMDID; ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, @@ -476,25 +476,25 @@ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) { struct ath11k *ar = hw->priv; - struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; + struct nlattr *tb[ATH_TM_ATTR_MAX + 1]; int ret; - ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy, + ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath11k_tm_policy, NULL); if (ret) return ret; - if (!tb[ATH11K_TM_ATTR_CMD]) + if (!tb[ATH_TM_ATTR_CMD]) return -EINVAL; - switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { - case ATH11K_TM_CMD_GET_VERSION: + switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) { + case ATH_TM_CMD_GET_VERSION: return ath11k_tm_cmd_get_version(ar, tb); - case ATH11K_TM_CMD_WMI: + case ATH_TM_CMD_WMI: return ath11k_tm_cmd_wmi(ar, tb, vif); - case ATH11K_TM_CMD_TESTMODE_START: + case ATH_TM_CMD_TESTMODE_START: return ath11k_tm_cmd_testmode_start(ar, tb); - case ATH11K_TM_CMD_WMI_FTM: + case ATH_TM_CMD_WMI_FTM: return ath11k_tm_cmd_wmi_ftm(ar, tb); default: return -EOPNOTSUPP; diff --git a/drivers/net/wireless/ath/ath11k/thermal.c b/drivers/net/wireless/ath/ath11k/thermal.c index 41e7499f075f..18d6eab5cce3 100644 --- a/drivers/net/wireless/ath/ath11k/thermal.c +++ b/drivers/net/wireless/ath/ath11k/thermal.c @@ -101,7 +101,7 @@ static ssize_t ath11k_thermal_show_temp(struct device *dev, spin_unlock_bh(&ar->data_lock); /* display in millidegree Celsius */ - ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); + ret = sysfs_emit(buf, "%d\n", temperature * 1000); out: mutex_unlock(&ar->conf_mutex); return ret; diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h index 235ab8ea715f..75246b0a82e3 100644 --- a/drivers/net/wireless/ath/ath11k/trace.h +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -48,8 +48,8 @@ TRACE_EVENT(ath11k_htt_pktlog, ), TP_fast_assign( - __assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->buf_len = buf_len; __entry->pktlog_checksum = pktlog_checksum; memcpy(__get_dynamic_array(pktlog), buf, buf_len); @@ -77,8 +77,8 @@ TRACE_EVENT(ath11k_htt_ppdu_stats, ), TP_fast_assign( - __assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; memcpy(__get_dynamic_array(ppdu), data, len); ), @@ -105,8 +105,8 @@ TRACE_EVENT(ath11k_htt_rxdesc, ), TP_fast_assign( - __assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; __entry->log_type = log_type; memcpy(__get_dynamic_array(rxdesc), data, len); @@ -130,8 +130,8 @@ DECLARE_EVENT_CLASS(ath11k_log_event, __vstring(msg, vaf->fmt, vaf->va) ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); + __assign_str(device); + __assign_str(driver); __assign_vstr(msg, vaf->fmt, vaf->va); ), TP_printk( @@ -171,8 +171,8 @@ TRACE_EVENT(ath11k_wmi_cmd, ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->id = id; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); @@ -201,8 +201,8 @@ TRACE_EVENT(ath11k_wmi_event, ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->id = id; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); @@ -230,8 +230,8 @@ TRACE_EVENT(ath11k_log_dbg, ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->level = level; WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), ATH11K_MSG_MAX, vaf->fmt, @@ -262,10 +262,10 @@ TRACE_EVENT(ath11k_log_dbg_dump, ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); - __assign_str(msg, msg); - __assign_str(prefix, prefix); + __assign_str(device); + __assign_str(driver); + __assign_str(msg); + __assign_str(prefix); __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); ), @@ -292,8 +292,8 @@ TRACE_EVENT(ath11k_wmi_diag, ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; memcpy(__get_dynamic_array(data), data, len); ), @@ -318,8 +318,8 @@ TRACE_EVENT(ath11k_ps_timekeeper, __field(u32, peer_ps_timestamp) ), - TP_fast_assign(__assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + TP_fast_assign(__assign_str(device); + __assign_str(driver); memcpy(__get_dynamic_array(peer_addr), peer_addr, ETH_ALEN); __entry->peer_ps_state = peer_ps_state; diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 34ab9631ff36..56af2e9634f4 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.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-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -20,6 +20,7 @@ #include "hw.h" #include "peer.h" #include "testmode.h" +#include "p2p.h" struct wmi_tlv_policy { size_t min_len; @@ -154,6 +155,10 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, [WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = { .min_len = sizeof(struct wmi_twt_add_dialog_event) }, + [WMI_TAG_P2P_NOA_INFO] = { + .min_len = sizeof(struct ath11k_wmi_p2p_noa_info) }, + [WMI_TAG_P2P_NOA_EVENT] = { + .min_len = sizeof(struct wmi_p2p_noa_event) }, }; #define PRIMAP(_hw_mode_) \ @@ -981,7 +986,7 @@ int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg, FIELD_PREP(WMI_TLV_LEN, 0); /* Note: This is a nested TLV containing: - * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv].. + * [wmi_tlv][ath11k_wmi_p2p_noa_descriptor][wmi_tlv].. */ ptr += sizeof(*tlv); @@ -1704,6 +1709,45 @@ int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, return ret; } +int ath11k_wmi_p2p_go_bcn_ie(struct ath11k *ar, u32 vdev_id, + const u8 *p2p_ie) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_p2p_go_set_beacon_ie_cmd *cmd; + size_t p2p_ie_len, aligned_len; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int ret, len; + + p2p_ie_len = p2p_ie[1] + 2; + aligned_len = roundup(p2p_ie_len, 4); + + len = sizeof(*cmd) + TLV_HDR_SIZE + aligned_len; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_p2p_go_set_beacon_ie_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_P2P_GO_SET_BEACON_IE) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->ie_buf_len = p2p_ie_len; + + tlv = (struct wmi_tlv *)cmd->tlv; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, aligned_len); + memcpy(tlv->value, p2p_ie, p2p_ie_len); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_P2P_GO_SET_BEACON_IE); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_P2P_GO_SET_BEACON_IE\n"); + dev_kfree_skb(skb); + } + + return ret; +} + int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, struct ieee80211_mutable_offsets *offs, struct sk_buff *bcn, u32 ema_params) @@ -2618,7 +2662,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, } int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, - struct wmi_wmm_params_all_arg *param) + struct wmi_wmm_params_all_arg *param, + enum wmi_wmm_params_type wmm_param_type) { struct ath11k_pdev_wmi *wmi = ar->wmi; struct wmi_vdev_set_wmm_params_cmd *cmd; @@ -2637,7 +2682,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); cmd->vdev_id = vdev_id; - cmd->wmm_param_type = 0; + cmd->wmm_param_type = wmm_param_type; for (ac = 0; ac < WME_NUM_AC; ac++) { switch (ac) { @@ -2670,8 +2715,8 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, wmm_param->no_ack = wmi_wmm_arg->no_ack; ath11k_dbg(ar->ab, ATH11K_DBG_WMI, - "wmm set ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", - ac, wmm_param->aifs, wmm_param->cwmin, + "wmm set type %d ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", + wmm_param_type, ac, wmm_param->aifs, wmm_param->cwmin, wmm_param->cwmax, wmm_param->txoplimit, wmm_param->acm, wmm_param->no_ack); } @@ -4020,7 +4065,8 @@ ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *sk switch (ev->evt_type) { case WMI_BSS_COLOR_COLLISION_DETECTION: - ieee80211_obss_color_collision_notify(arvif->vif, ev->obss_color_bitmap); + ieee80211_obss_color_collision_notify(arvif->vif, ev->obss_color_bitmap, + 0); ath11k_dbg(ab, ATH11K_DBG_WMI, "OBSS color collision detected vdev:%d, event:%d, bitmap:%08llx\n", ev->vdev_id, ev->evt_type, ev->obss_color_bitmap); @@ -7359,7 +7405,9 @@ static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *s rcu_read_unlock(); return; } - ath11k_mac_bcn_tx_event(arvif); + + queue_work(ab->workqueue, &arvif->bcn_tx_work); + rcu_read_unlock(); } @@ -8110,6 +8158,11 @@ static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *skb) { struct ath11k_fw_stats stats = {}; + size_t total_vdevs_started = 0; + struct ath11k_pdev *pdev; + bool is_end = true; + int i; + struct ath11k *ar; int ret; @@ -8136,25 +8189,57 @@ static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *sk spin_lock_bh(&ar->data_lock); - /* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via + /* WMI_REQUEST_PDEV_STAT, WMI_REQUEST_VDEV_STAT and + * WMI_REQUEST_RSSI_PER_CHAIN_STAT can be requested via mac ops or via * debugfs fw stats. Therefore, processing it separately. */ if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs); - ar->fw_stats_done = true; + complete(&ar->fw_stats_done); goto complete; } - /* WMI_REQUEST_VDEV_STAT, WMI_REQUEST_BCN_STAT and WMI_REQUEST_RSSI_PER_CHAIN_STAT - * are currently requested only via debugfs fw stats. Hence, processing these - * in debugfs context + if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) { + complete(&ar->fw_stats_done); + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats.vdevs)) { + ath11k_warn(ab, "empty vdev stats"); + goto complete; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += ar->num_started_vdevs; + } + + if (total_vdevs_started) + is_end = ((++ar->fw_stats.num_vdev_recvd) == + total_vdevs_started); + + list_splice_tail_init(&stats.vdevs, + &ar->fw_stats.vdevs); + + if (is_end) + complete(&ar->fw_stats_done); + + goto complete; + } + + /* WMI_REQUEST_BCN_STAT is currently requested only via debugfs fw stats. + * Hence, processing it in debugfs context */ ath11k_debugfs_fw_stats_process(ar, &stats); complete: complete(&ar->fw_stats_complete); - rcu_read_unlock(); spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); /* Since the stats's pdev, vdev and beacon list are spliced and reinitialised * at this point, no need to free the individual list. @@ -8311,7 +8396,7 @@ ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff if (ar->dfs_block_radar_events) ath11k_info(ab, "DFS Radar detected, but ignored as requested\n"); else - ieee80211_radar_detected(ar->hw); + ieee80211_radar_detected(ar->hw, NULL); exit: rcu_read_unlock(); @@ -8606,6 +8691,58 @@ exit: kfree(tb); } +static void ath11k_wmi_p2p_noa_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_p2p_noa_event *ev; + const struct ath11k_wmi_p2p_noa_info *noa; + struct ath11k *ar; + int vdev_id; + u8 noa_descriptors; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ath11k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb)); + return; + } + + ev = tb[WMI_TAG_P2P_NOA_EVENT]; + noa = tb[WMI_TAG_P2P_NOA_INFO]; + + if (!ev || !noa) + goto out; + + vdev_id = ev->vdev_id; + noa_descriptors = u32_get_bits(noa->noa_attr, + WMI_P2P_NOA_INFO_DESC_NUM); + + if (noa_descriptors > WMI_P2P_MAX_NOA_DESCRIPTORS) { + ath11k_warn(ab, "invalid descriptor num %d in P2P NoA event\n", + noa_descriptors); + goto out; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi tlv p2p noa vdev_id %i descriptors %u\n", + vdev_id, noa_descriptors); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id %d in P2P NoA event\n", + vdev_id); + goto unlock; + } + + ath11k_p2p_noa_update_by_vdev_id(ar, vdev_id, noa); + +unlock: + rcu_read_unlock(); +out: + kfree(tb); +} + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -8733,6 +8870,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_GTK_OFFLOAD_STATUS_EVENTID: ath11k_wmi_gtk_offload_status_event(ab, skb); break; + case WMI_P2P_NOA_EVENTID: + ath11k_wmi_p2p_noa_event(ab, skb); + break; default: ath11k_dbg(ab, ATH11K_DBG_WMI, "unsupported event id 0x%x\n", id); break; @@ -8982,7 +9122,7 @@ int ath11k_wmi_attach(struct ath11k_base *ab) ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; /* It's overwritten when service_ext_ready is handled */ - if (ab->hw_params.single_pdev_only && ab->hw_params.num_rxmda_per_pdev > 1) + if (ab->hw_params.single_pdev_only && ab->hw_params.num_rxdma_per_pdev > 1) ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_SINGLE; /* TODO: Init remaining wmi soc resources required */ diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index bb419e3abb00..9fcffaa2f383 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_WMI_H @@ -60,13 +60,9 @@ struct wmi_tlv { #define WLAN_SCAN_MAX_HINT_BSSID 10 #define MAX_RNR_BSS 5 -#define WLAN_SCAN_MAX_HINT_S_SSID 10 -#define WLAN_SCAN_MAX_HINT_BSSID 10 -#define MAX_RNR_BSS 5 - #define WLAN_SCAN_PARAMS_MAX_SSID 16 #define WLAN_SCAN_PARAMS_MAX_BSSID 4 -#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 512 #define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1 @@ -3444,34 +3440,6 @@ struct wmi_bssid_arg { const u8 *bssid; }; -struct wmi_start_scan_arg { - u32 scan_id; - u32 scan_req_id; - u32 vdev_id; - u32 scan_priority; - u32 notify_scan_events; - u32 dwell_time_active; - u32 dwell_time_passive; - u32 min_rest_time; - u32 max_rest_time; - u32 repeat_probe_time; - u32 probe_spacing_time; - u32 idle_time; - u32 max_scan_time; - u32 probe_delay; - u32 scan_ctrl_flags; - - u32 ie_len; - u32 n_channels; - u32 n_ssids; - u32 n_bssids; - - u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; - u32 channels[64]; - struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; - struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; -}; - #define WMI_SCAN_STOP_ONE 0x00000000 #define WMI_SCN_STOP_VAP_ALL 0x01000000 #define WMI_SCAN_STOP_ALL 0x04000000 @@ -3630,6 +3598,37 @@ struct wmi_ftm_event_msg { u8 data[]; } __packed; +#define WMI_P2P_MAX_NOA_DESCRIPTORS 4 + +struct wmi_p2p_noa_event { + u32 vdev_id; +} __packed; + +struct ath11k_wmi_p2p_noa_descriptor { + u32 type_count; /* 255: continuous schedule, 0: reserved */ + u32 duration; /* Absent period duration in micro seconds */ + u32 interval; /* Absent period interval in micro seconds */ + u32 start_time; /* 32 bit tsf time when in starts */ +} __packed; + +#define WMI_P2P_NOA_INFO_CHANGED_FLAG BIT(0) +#define WMI_P2P_NOA_INFO_INDEX GENMASK(15, 8) +#define WMI_P2P_NOA_INFO_OPP_PS BIT(16) +#define WMI_P2P_NOA_INFO_CTWIN_TU GENMASK(23, 17) +#define WMI_P2P_NOA_INFO_DESC_NUM GENMASK(31, 24) + +struct ath11k_wmi_p2p_noa_info { + /* Bit 0 - Flag to indicate an update in NOA schedule + * Bits 7-1 - Reserved + * Bits 15-8 - Index (identifies the instance of NOA sub element) + * Bit 16 - Opp PS state of the AP + * Bits 23-17 - Ctwindow in TUs + * Bits 31-24 - Number of NOA descriptors + */ + u32 noa_attr; + struct ath11k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS]; +} __packed; + #define WMI_BEACON_TX_BUFFER_SIZE 512 #define WMI_EMA_TMPL_IDX_SHIFT 8 @@ -3653,6 +3652,13 @@ struct wmi_bcn_tmpl_cmd { u32 ema_params; } __packed; +struct wmi_p2p_go_set_beacon_ie_cmd { + u32 tlv_header; + u32 vdev_id; + u32 ie_buf_len; + u8 tlv[]; +} __packed; + struct wmi_key_seq_counter { u32 key_seq_counter_l; u32 key_seq_counter_h; @@ -3811,6 +3817,7 @@ struct wmi_stop_scan_cmd { }; struct scan_chan_list_params { + struct list_head list; u32 pdev_id; u16 nallchans; struct channel_param ch_param[]; @@ -5740,8 +5747,6 @@ struct wmi_debug_log_config_cmd_fixed_param { u32 value; } __packed; -#define WMI_MAX_MEM_REQS 32 - #define MAX_RADIOS 3 #define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ) @@ -6342,6 +6347,11 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 +enum wmi_wmm_params_type { + WMI_WMM_PARAM_TYPE_LEGACY = 0, + WMI_WMM_PARAM_TYPE_11AX_MU_EDCA = 1, +}; + const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, struct sk_buff *skb, gfp_t gfp); int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, @@ -6349,6 +6359,8 @@ int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, struct sk_buff *frame); +int ath11k_wmi_p2p_go_bcn_ie(struct ath11k *ar, u32 vdev_id, + const u8 *p2p_ie); int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, struct ieee80211_mutable_offsets *offs, struct sk_buff *bcn, u32 ema_param); @@ -6396,7 +6408,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, struct scan_cancel_param *param); int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, - struct wmi_wmm_params_all_arg *param); + struct wmi_wmm_params_all_arg *param, + enum wmi_wmm_params_type wmm_param_type); int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, u32 pdev_id); int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id); diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c index 99d8ba45a75b..b6f08755129f 100644 --- a/drivers/net/wireless/ath/ath11k/wow.c +++ b/drivers/net/wireless/ath/ath11k/wow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/delay.h> @@ -148,13 +148,16 @@ static int ath11k_wow_cleanup(struct ath11k *ar) * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | * +--+------------+----+-----------+---------------+-----------+ */ -static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, - const struct cfg80211_pkt_pattern *old) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, + const struct cfg80211_pkt_pattern *old) { u8 hdr_8023_pattern[ETH_HLEN] = {}; u8 hdr_8023_bit_mask[ETH_HLEN] = {}; u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; + u8 bytemask[WOW_MAX_PATTERN_SIZE] = {}; int total_len = old->pkt_offset + old->pattern_len; int hdr_80211_end_offset; @@ -172,11 +175,17 @@ static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, struct rfc1042_hdr *new_rfc_mask = (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); int rfc_len = sizeof(*new_rfc_pattern); + int i; + + /* convert bitmask to bytemask */ + for (i = 0; i < old->pattern_len; i++) + if (old->mask[i / 8] & BIT(i % 8)) + bytemask[i] = 0xff; memcpy(hdr_8023_pattern + old->pkt_offset, old->pattern, ETH_HLEN - old->pkt_offset); memcpy(hdr_8023_bit_mask + old->pkt_offset, - old->mask, ETH_HLEN - old->pkt_offset); + bytemask, ETH_HLEN - old->pkt_offset); /* Copy destination address */ memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); @@ -232,7 +241,7 @@ static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, (void *)old->pattern + ETH_HLEN - old->pkt_offset, total_len - ETH_HLEN); memcpy((u8 *)new->mask + new->pattern_len, - (void *)old->mask + ETH_HLEN - old->pkt_offset, + bytemask + ETH_HLEN - old->pkt_offset, total_len - ETH_HLEN); new->pattern_len += total_len - ETH_HLEN; @@ -393,35 +402,31 @@ static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif, } for (i = 0; i < wowlan->n_patterns; i++) { - u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; struct cfg80211_pkt_pattern new_pattern = {}; - struct cfg80211_pkt_pattern old_pattern = patterns[i]; - int j; new_pattern.pattern = ath_pattern; new_pattern.mask = ath_bitmask; if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) continue; - /* convert bytemask to bitmask */ - for (j = 0; j < patterns[i].pattern_len; j++) - if (patterns[i].mask[j / 8] & BIT(j % 8)) - bitmask[j] = 0xff; - old_pattern.mask = bitmask; if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == ATH11K_HW_TXRX_NATIVE_WIFI) { if (patterns[i].pkt_offset < ETH_HLEN) { - u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {}; - - memcpy(pattern_ext, old_pattern.pattern, - old_pattern.pattern_len); - old_pattern.pattern = pattern_ext; ath11k_wow_convert_8023_to_80211(&new_pattern, - &old_pattern); + &patterns[i]); } else { - new_pattern = old_pattern; + int j; + + new_pattern = patterns[i]; + new_pattern.mask = ath_bitmask; + + /* convert bitmask to bytemask */ + for (j = 0; j < patterns[i].pattern_len; j++) + if (patterns[i].mask[j / 8] & BIT(j % 8)) + ath_bitmask[j] = 0xff; + new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; } } diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig index e135d2b1b61d..1ea1af1b8f6c 100644 --- a/drivers/net/wireless/ath/ath12k/Kconfig +++ b/drivers/net/wireless/ath/ath12k/Kconfig @@ -7,6 +7,7 @@ config ATH12K select MHI_BUS select QRTR select QRTR_MHI + select PCI_PWRCTRL_PWRSEQ if HAVE_PWRCTRL help Enable support for Qualcomm Technologies Wi-Fi 7 (IEEE 802.11be) family of chipsets, for example WCN7850 and @@ -14,6 +15,14 @@ config ATH12K If you choose to build a module, it'll be called ath12k. +config ATH12K_AHB + bool "QTI ath12k AHB support" + depends on ATH12K && REMOTEPROC + select QCOM_MDT_LOADER + select QCOM_SCM + help + Enable support for Ath12k AHB bus chipsets, example IPQ5332. + config ATH12K_DEBUG bool "ath12k debugging" depends on ATH12K @@ -24,6 +33,15 @@ config ATH12K_DEBUG If unsure, say Y to make it easier to debug problems. But if you want optimal performance choose N. +config ATH12K_DEBUGFS + bool "QTI ath12k debugfs support" + depends on ATH12K && MAC80211_DEBUGFS + help + Enable ath12k debugfs support + + If unsure, say Y to make it easier to debug problems. But if + you want optimal performance choose N. + config ATH12K_TRACING bool "ath12k tracing support" depends on ATH12K && EVENT_TRACING @@ -32,3 +50,13 @@ config ATH12K_TRACING If unsure, say Y to make it easier to debug problems. But if you want optimal performance choose N. + +config ATH12K_COREDUMP + bool "ath12k coredump" + depends on ATH12K + select WANT_DEV_COREDUMP + help + Enable ath12k coredump collection + + If unsure, say Y to make it easier to debug problems. But if + dump collection not required choose N. diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index 71669f94ff75..d95ee525a6cd 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -23,7 +23,13 @@ ath12k-y += core.o \ fw.o \ p2p.o +ath12k-$(CONFIG_ATH12K_AHB) += ahb.o +ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o +ath12k-$(CONFIG_ACPI) += acpi.o ath12k-$(CONFIG_ATH12K_TRACING) += trace.o +ath12k-$(CONFIG_PM) += wow.o +ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o +ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c new file mode 100644 index 000000000000..d81367ce6929 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "core.h" +#include "acpi.h" +#include "debug.h" + +static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) +{ + union acpi_object *obj; + acpi_handle root_handle; + int ret, i; + + root_handle = ACPI_HANDLE(ab->dev); + if (!root_handle) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "invalid acpi handler\n"); + return -EOPNOTSUPP; + } + + obj = acpi_evaluate_dsm(root_handle, ab->hw_params->acpi_guid, 0, func, + NULL); + + if (!obj) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi_evaluate_dsm() failed\n"); + return -ENOENT; + } + + if (obj->type == ACPI_TYPE_INTEGER) { + switch (func) { + case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: + ab->acpi.func_bit = obj->integer.value; + break; + case ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG: + ab->acpi.bit_flag = obj->integer.value; + break; + } + } else if (obj->type == ACPI_TYPE_STRING) { + switch (func) { + case ATH12K_ACPI_DSM_FUNC_BDF_EXT: + if (obj->string.length <= ATH12K_ACPI_BDF_ANCHOR_STRING_LEN || + obj->string.length > ATH12K_ACPI_BDF_MAX_LEN || + memcmp(obj->string.pointer, ATH12K_ACPI_BDF_ANCHOR_STRING, + ATH12K_ACPI_BDF_ANCHOR_STRING_LEN)) { + ath12k_warn(ab, "invalid ACPI DSM BDF size: %d\n", + obj->string.length); + ret = -EINVAL; + goto out; + } + + memcpy(ab->acpi.bdf_string, obj->string.pointer, + obj->buffer.length); + + break; + } + } else if (obj->type == ACPI_TYPE_BUFFER) { + switch (func) { + case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: + if (obj->buffer.length < ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE || + obj->buffer.length > ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM func size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + ab->acpi.func_bit = 0; + for (i = 0; i < obj->buffer.length; i++) + ab->acpi.func_bit += obj->buffer.pointer[i] << (i * 8); + + break; + case ATH12K_ACPI_DSM_FUNC_TAS_CFG: + if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_CFG_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM TAS config size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + memcpy(&ab->acpi.tas_cfg, obj->buffer.pointer, + obj->buffer.length); + + break; + case ATH12K_ACPI_DSM_FUNC_TAS_DATA: + if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_DATA_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM TAS data size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + memcpy(&ab->acpi.tas_sar_power_table, obj->buffer.pointer, + obj->buffer.length); + + break; + case ATH12K_ACPI_DSM_FUNC_BIOS_SAR: + if (obj->buffer.length != ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE) { + ath12k_warn(ab, "invalid ACPI BIOS SAR data size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + memcpy(&ab->acpi.bios_sar_data, obj->buffer.pointer, + obj->buffer.length); + + break; + case ATH12K_ACPI_DSM_FUNC_GEO_OFFSET: + if (obj->buffer.length != ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE) { + ath12k_warn(ab, "invalid ACPI GEO OFFSET data size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + memcpy(&ab->acpi.geo_offset_data, obj->buffer.pointer, + obj->buffer.length); + + break; + case ATH12K_ACPI_DSM_FUNC_INDEX_CCA: + if (obj->buffer.length != ATH12K_ACPI_DSM_CCA_DATA_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM CCA data size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + memcpy(&ab->acpi.cca_data, obj->buffer.pointer, + obj->buffer.length); + + break; + case ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE: + if (obj->buffer.length != ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM band edge data size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + memcpy(&ab->acpi.band_edge_power, obj->buffer.pointer, + obj->buffer.length); + + break; + } + } else { + ath12k_warn(ab, "ACPI DSM method returned an unsupported object type: %d\n", + obj->type); + ret = -EINVAL; + goto out; + } + + ret = 0; + +out: + ACPI_FREE(obj); + return ret; +} + +static int ath12k_acpi_set_power_limit(struct ath12k_base *ab) +{ + const u8 *tas_sar_power_table = ab->acpi.tas_sar_power_table; + int ret; + + if (tas_sar_power_table[0] != ATH12K_ACPI_TAS_DATA_VERSION || + tas_sar_power_table[1] != ATH12K_ACPI_TAS_DATA_ENABLE) { + ath12k_warn(ab, "latest ACPI TAS data is invalid\n"); + return -EINVAL; + } + + ret = ath12k_wmi_set_bios_cmd(ab, WMI_BIOS_PARAM_TAS_DATA_TYPE, + tas_sar_power_table, + ATH12K_ACPI_DSM_TAS_DATA_SIZE); + if (ret) { + ath12k_warn(ab, "failed to send ACPI TAS data table: %d\n", ret); + return ret; + } + + return ret; +} + +static int ath12k_acpi_set_bios_sar_power(struct ath12k_base *ab) +{ + int ret; + + if (ab->acpi.bios_sar_data[0] != ATH12K_ACPI_POWER_LIMIT_VERSION || + ab->acpi.bios_sar_data[1] != ATH12K_ACPI_POWER_LIMIT_ENABLE_FLAG) { + ath12k_warn(ab, "invalid latest ACPI BIOS SAR data\n"); + return -EINVAL; + } + + ret = ath12k_wmi_set_bios_sar_cmd(ab, ab->acpi.bios_sar_data); + if (ret) { + ath12k_warn(ab, "failed to set ACPI BIOS SAR table: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath12k_acpi_dsm_notify(acpi_handle handle, u32 event, void *data) +{ + int ret; + struct ath12k_base *ab = data; + + if (event == ATH12K_ACPI_NOTIFY_EVENT) { + ath12k_warn(ab, "unknown acpi notify %u\n", event); + return; + } + + if (!ab->acpi.acpi_tas_enable) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi_tas_enable is false\n"); + return; + } + + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_DATA); + if (ret) { + ath12k_warn(ab, "failed to update ACPI TAS data table: %d\n", ret); + return; + } + + ret = ath12k_acpi_set_power_limit(ab); + if (ret) { + ath12k_warn(ab, "failed to set ACPI TAS power limit data: %d", ret); + return; + } + + if (!ab->acpi.acpi_bios_sar_enable) + return; + + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BIOS_SAR); + if (ret) { + ath12k_warn(ab, "failed to update BIOS SAR: %d\n", ret); + return; + } + + ret = ath12k_acpi_set_bios_sar_power(ab); + if (ret) { + ath12k_warn(ab, "failed to set BIOS SAR power limit: %d\n", ret); + return; + } +} + +static int ath12k_acpi_set_bios_sar_params(struct ath12k_base *ab) +{ + int ret; + + ret = ath12k_wmi_set_bios_sar_cmd(ab, ab->acpi.bios_sar_data); + if (ret) { + ath12k_warn(ab, "failed to set ACPI BIOS SAR table: %d\n", ret); + return ret; + } + + ret = ath12k_wmi_set_bios_geo_cmd(ab, ab->acpi.geo_offset_data); + if (ret) { + ath12k_warn(ab, "failed to set ACPI BIOS GEO table: %d\n", ret); + return ret; + } + + return 0; +} + +static int ath12k_acpi_set_tas_params(struct ath12k_base *ab) +{ + int ret; + + ret = ath12k_wmi_set_bios_cmd(ab, WMI_BIOS_PARAM_TAS_CONFIG_TYPE, + ab->acpi.tas_cfg, + ATH12K_ACPI_DSM_TAS_CFG_SIZE); + if (ret) { + ath12k_warn(ab, "failed to send ACPI TAS config table parameter: %d\n", + ret); + return ret; + } + + ret = ath12k_wmi_set_bios_cmd(ab, WMI_BIOS_PARAM_TAS_DATA_TYPE, + ab->acpi.tas_sar_power_table, + ATH12K_ACPI_DSM_TAS_DATA_SIZE); + if (ret) { + ath12k_warn(ab, "failed to send ACPI TAS data table parameter: %d\n", + ret); + return ret; + } + + return 0; +} + +bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab) +{ + return ab->acpi.acpi_disable_rfkill; +} + +bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) +{ + return ab->acpi.acpi_disable_11be; +} + +void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) +{ + int ret; + u8 *buf; + + if (!ab->hw_params->acpi_guid) + /* not supported with this hardware */ + return; + + if (ab->acpi.acpi_tas_enable) { + ret = ath12k_acpi_set_tas_params(ab); + if (ret) { + ath12k_warn(ab, "failed to send ACPI TAS parameters: %d\n", ret); + return; + } + } + + if (ab->acpi.acpi_bios_sar_enable) { + ret = ath12k_acpi_set_bios_sar_params(ab); + if (ret) { + ath12k_warn(ab, "failed to send ACPI BIOS SAR: %d\n", ret); + return; + } + } + + if (ab->acpi.acpi_cca_enable) { + buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET; + ret = ath12k_wmi_set_bios_cmd(ab, + WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE, + buf, + ATH12K_ACPI_CCA_THR_OFFSET_LEN); + if (ret) { + ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n", + ret); + return; + } + } + + if (ab->acpi.acpi_band_edge_enable) { + ret = ath12k_wmi_set_bios_cmd(ab, + WMI_BIOS_PARAM_TYPE_BANDEDGE, + ab->acpi.band_edge_power, + sizeof(ab->acpi.band_edge_power)); + if (ret) { + ath12k_warn(ab, + "failed to set ACPI DSM band edge channel power: %d\n", + ret); + return; + } + } +} + +int ath12k_acpi_start(struct ath12k_base *ab) +{ + acpi_status status; + int ret; + + ab->acpi.acpi_tas_enable = false; + ab->acpi.acpi_disable_11be = false; + ab->acpi.acpi_disable_rfkill = false; + ab->acpi.acpi_bios_sar_enable = false; + ab->acpi.acpi_cca_enable = false; + ab->acpi.acpi_band_edge_enable = false; + ab->acpi.acpi_enable_bdf = false; + ab->acpi.bdf_string[0] = '\0'; + + if (!ab->hw_params->acpi_guid) + /* not supported with this hardware */ + return 0; + + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS); + if (ret) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to get ACPI DSM data: %d\n", ret); + return ret; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG); + if (ret) { + ath12k_warn(ab, "failed to get ACPI DISABLE FLAG: %d\n", ret); + return ret; + } + + if (ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi, + ATH12K_ACPI_DSM_DISABLE_11BE_BIT)) + ab->acpi.acpi_disable_11be = true; + + if (!ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi, + ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT)) + ab->acpi.acpi_disable_rfkill = true; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BDF_EXT)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BDF_EXT); + if (ret || ab->acpi.bdf_string[0] == '\0') { + ath12k_warn(ab, "failed to get ACPI BDF EXT: %d\n", ret); + return ret; + } + + ab->acpi.acpi_enable_bdf = true; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_CFG); + if (ret) { + ath12k_warn(ab, "failed to get ACPI TAS config table: %d\n", ret); + return ret; + } + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_DATA)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_DATA); + if (ret) { + ath12k_warn(ab, "failed to get ACPI TAS data table: %d\n", ret); + return ret; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG) && + ab->acpi.tas_sar_power_table[0] == ATH12K_ACPI_TAS_DATA_VERSION && + ab->acpi.tas_sar_power_table[1] == ATH12K_ACPI_TAS_DATA_ENABLE) + ab->acpi.acpi_tas_enable = true; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BIOS_SAR)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BIOS_SAR); + if (ret) { + ath12k_warn(ab, "failed to get ACPI bios sar data: %d\n", ret); + return ret; + } + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_GEO_OFFSET)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_GEO_OFFSET); + if (ret) { + ath12k_warn(ab, "failed to get ACPI geo offset data: %d\n", ret); + return ret; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BIOS_SAR) && + ab->acpi.bios_sar_data[0] == ATH12K_ACPI_POWER_LIMIT_VERSION && + ab->acpi.bios_sar_data[1] == ATH12K_ACPI_POWER_LIMIT_ENABLE_FLAG && + !ab->acpi.acpi_tas_enable) + ab->acpi.acpi_bios_sar_enable = true; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_CCA)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_CCA); + if (ret) { + ath12k_warn(ab, "failed to get ACPI DSM CCA threshold configuration: %d\n", + ret); + return ret; + } + + if (ab->acpi.cca_data[0] == ATH12K_ACPI_CCA_THR_VERSION && + ab->acpi.cca_data[ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET] == + ATH12K_ACPI_CCA_THR_ENABLE_FLAG) + ab->acpi.acpi_cca_enable = true; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, + ATH12K_ACPI_FUNC_BIT_BAND_EDGE_CHAN_POWER)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE); + if (ret) { + ath12k_warn(ab, "failed to get ACPI DSM band edge channel power: %d\n", + ret); + return ret; + } + + if (ab->acpi.band_edge_power[0] == ATH12K_ACPI_BAND_EDGE_VERSION && + ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) + ab->acpi.acpi_band_edge_enable = true; + } + + status = acpi_install_notify_handler(ACPI_HANDLE(ab->dev), + ACPI_DEVICE_NOTIFY, + ath12k_acpi_dsm_notify, ab); + if (ACPI_FAILURE(status)) { + ath12k_warn(ab, "failed to install DSM notify callback: %d\n", status); + return -EIO; + } + + ab->acpi.started = true; + + return 0; +} + +int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab) +{ + size_t max_len = sizeof(ab->qmi.target.bdf_ext); + + if (!ab->acpi.acpi_enable_bdf) + return -ENODATA; + + if (strscpy(ab->qmi.target.bdf_ext, ab->acpi.bdf_string + 4, max_len) < 0) + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "acpi bdf variant longer than the buffer (variant: %s)\n", + ab->acpi.bdf_string); + + return 0; +} + +void ath12k_acpi_stop(struct ath12k_base *ab) +{ + if (!ab->acpi.started) + return; + + acpi_remove_notify_handler(ACPI_HANDLE(ab->dev), + ACPI_DEVICE_NOTIFY, + ath12k_acpi_dsm_notify); + + memset(&ab->acpi, 0, sizeof(ab->acpi)); +} diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h new file mode 100644 index 000000000000..3a26fea6af1a --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef ATH12K_ACPI_H +#define ATH12K_ACPI_H + +#include <linux/acpi.h> + +#define ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS 0 +#define ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG 2 +#define ATH12K_ACPI_DSM_FUNC_BDF_EXT 3 +#define ATH12K_ACPI_DSM_FUNC_BIOS_SAR 4 +#define ATH12K_ACPI_DSM_FUNC_GEO_OFFSET 5 +#define ATH12K_ACPI_DSM_FUNC_INDEX_CCA 6 +#define ATH12K_ACPI_DSM_FUNC_TAS_CFG 8 +#define ATH12K_ACPI_DSM_FUNC_TAS_DATA 9 +#define ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE 10 + +#define ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG BIT(1) +#define ATH12K_ACPI_FUNC_BIT_BDF_EXT BIT(2) +#define ATH12K_ACPI_FUNC_BIT_BIOS_SAR BIT(3) +#define ATH12K_ACPI_FUNC_BIT_GEO_OFFSET BIT(4) +#define ATH12K_ACPI_FUNC_BIT_CCA BIT(5) +#define ATH12K_ACPI_FUNC_BIT_TAS_CFG BIT(7) +#define ATH12K_ACPI_FUNC_BIT_TAS_DATA BIT(8) +#define ATH12K_ACPI_FUNC_BIT_BAND_EDGE_CHAN_POWER BIT(9) + +#define ATH12K_ACPI_NOTIFY_EVENT 0x86 +#define ATH12K_ACPI_FUNC_BIT_VALID(_acdata, _func) (((_acdata).func_bit) & (_func)) +#define ATH12K_ACPI_CHEK_BIT_VALID(_acdata, _func) (((_acdata).bit_flag) & (_func)) + +#define ATH12K_ACPI_TAS_DATA_VERSION 0x1 +#define ATH12K_ACPI_TAS_DATA_ENABLE 0x1 +#define ATH12K_ACPI_POWER_LIMIT_VERSION 0x1 +#define ATH12K_ACPI_POWER_LIMIT_ENABLE_FLAG 0x1 +#define ATH12K_ACPI_CCA_THR_VERSION 0x1 +#define ATH12K_ACPI_CCA_THR_ENABLE_FLAG 0x1 +#define ATH12K_ACPI_BAND_EDGE_VERSION 0x1 +#define ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG 0x1 + +#define ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET 1 +#define ATH12K_ACPI_DBS_BACKOFF_DATA_OFFSET 2 +#define ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET 5 +#define ATH12K_ACPI_BIOS_SAR_DBS_BACKOFF_LEN 10 +#define ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET 12 +#define ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN 18 +#define ATH12K_ACPI_BIOS_SAR_TABLE_LEN 22 +#define ATH12K_ACPI_CCA_THR_OFFSET_LEN 36 + +#define ATH12K_ACPI_DSM_TAS_DATA_SIZE 69 +#define ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE 100 +#define ATH12K_ACPI_DSM_TAS_CFG_SIZE 108 + +#define ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE 1 +#define ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE 4 + +#define ATH12K_ACPI_DSM_DISABLE_11BE_BIT BIT(0) +#define ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT BIT(2) + +#define ATH12K_ACPI_BDF_ANCHOR_STRING_LEN 3 +#define ATH12K_ACPI_BDF_ANCHOR_STRING "BDF" +#define ATH12K_ACPI_BDF_MAX_LEN 100 + +#define ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE (ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET + \ + ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN) +#define ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE (ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET + \ + ATH12K_ACPI_BIOS_SAR_TABLE_LEN) +#define ATH12K_ACPI_DSM_CCA_DATA_SIZE (ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET + \ + ATH12K_ACPI_CCA_THR_OFFSET_LEN) + +#ifdef CONFIG_ACPI + +int ath12k_acpi_start(struct ath12k_base *ab); +void ath12k_acpi_stop(struct ath12k_base *ab); +bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab); +bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab); +void ath12k_acpi_set_dsm_func(struct ath12k_base *ab); +int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab); + +#else + +static inline int ath12k_acpi_start(struct ath12k_base *ab) +{ + return 0; +} + +static inline void ath12k_acpi_stop(struct ath12k_base *ab) +{ +} + +static inline bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab) +{ + return false; +} + +static inline bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) +{ + return false; +} + +static inline void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) +{ +} + +static inline int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab) +{ + return 0; +} + +#endif /* CONFIG_ACPI */ + +#endif /* ATH12K_ACPI_H */ diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c new file mode 100644 index 000000000000..8d1a86e420a4 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -0,0 +1,1155 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/dma-mapping.h> +#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/soc/qcom/mdt_loader.h> +#include <linux/soc/qcom/smem_state.h> +#include "ahb.h" +#include "debug.h" +#include "hif.h" + +static const struct of_device_id ath12k_ahb_of_match[] = { + { .compatible = "qcom,ipq5332-wifi", + .data = (void *)ATH12K_HW_IPQ5332_HW10, + }, + { } +}; + +MODULE_DEVICE_TABLE(of, ath12k_ahb_of_match); + +#define ATH12K_IRQ_CE0_OFFSET 4 +#define ATH12K_MAX_UPDS 1 +#define ATH12K_UPD_IRQ_WRD_LEN 18 +static const char ath12k_userpd_irq[][9] = {"spawn", + "ready", + "stop-ack"}; + +static const char *irq_name[ATH12K_IRQ_NUM_MAX] = { + "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring4", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring", +}; + +enum ext_irq_num { + host2wbm_desc_feed = 16, + host2reo_re_injection, + host2reo_command, + host2rxdma_monitor_ring3, + host2rxdma_monitor_ring2, + host2rxdma_monitor_ring1, + reo2host_exception, + wbm2host_rx_release, + reo2host_status, + reo2host_destination_ring4, + reo2host_destination_ring3, + reo2host_destination_ring2, + reo2host_destination_ring1, + rxdma2host_monitor_destination_mac3, + rxdma2host_monitor_destination_mac2, + rxdma2host_monitor_destination_mac1, + ppdu_end_interrupts_mac3, + ppdu_end_interrupts_mac2, + ppdu_end_interrupts_mac1, + rxdma2host_monitor_status_ring_mac3, + rxdma2host_monitor_status_ring_mac2, + rxdma2host_monitor_status_ring_mac1, + host2rxdma_host_buf_ring_mac3, + host2rxdma_host_buf_ring_mac2, + host2rxdma_host_buf_ring_mac1, + rxdma2host_destination_ring_mac3, + rxdma2host_destination_ring_mac2, + rxdma2host_destination_ring_mac1, + host2tcl_input_ring4, + host2tcl_input_ring3, + host2tcl_input_ring2, + host2tcl_input_ring1, + wbm2host_tx_completions_ring4, + wbm2host_tx_completions_ring3, + wbm2host_tx_completions_ring2, + wbm2host_tx_completions_ring1, + tcl2host_status_ring, +}; + +static u32 ath12k_ahb_read32(struct ath12k_base *ab, u32 offset) +{ + if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET) + return ioread32(ab->mem_ce + offset); + return ioread32(ab->mem + offset); +} + +static void ath12k_ahb_write32(struct ath12k_base *ab, u32 offset, + u32 value) +{ + if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET) + iowrite32(value, ab->mem_ce + offset); + else + iowrite32(value, ab->mem + offset); +} + +static void ath12k_ahb_cancel_workqueue(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + cancel_work_sync(&ce_pipe->intr_wq); + } +} + +static void ath12k_ahb_ext_grp_disable(struct ath12k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void __ath12k_ahb_ext_irq_disable(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + ath12k_ahb_ext_grp_disable(irq_grp); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } + } +} + +static void ath12k_ahb_ext_grp_enable(struct ath12k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void ath12k_ahb_setbit32(struct ath12k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath12k_ahb_read32(ab, offset); + ath12k_ahb_write32(ab, offset, val | BIT(bit)); +} + +static void ath12k_ahb_clearbit32(struct ath12k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath12k_ahb_read32(ab, offset); + ath12k_ahb_write32(ab, offset, val & ~BIT(bit)); +} + +static void ath12k_ahb_ce_irq_enable(struct ath12k_base *ab, u16 ce_id) +{ + const struct ce_attr *ce_attr; + const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + + ie1_reg_addr = ce_ie_addr->ie1_reg_addr; + ie2_reg_addr = ce_ie_addr->ie2_reg_addr; + ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params->host_ce_config[ce_id]; + if (ce_attr->src_nentries) + ath12k_ahb_setbit32(ab, ce_id, ie1_reg_addr); + + if (ce_attr->dest_nentries) { + ath12k_ahb_setbit32(ab, ce_id, ie2_reg_addr); + ath12k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + ie3_reg_addr); + } +} + +static void ath12k_ahb_ce_irq_disable(struct ath12k_base *ab, u16 ce_id) +{ + const struct ce_attr *ce_attr; + const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + + ie1_reg_addr = ce_ie_addr->ie1_reg_addr; + ie2_reg_addr = ce_ie_addr->ie2_reg_addr; + ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params->host_ce_config[ce_id]; + if (ce_attr->src_nentries) + ath12k_ahb_clearbit32(ab, ce_id, ie1_reg_addr); + + if (ce_attr->dest_nentries) { + ath12k_ahb_clearbit32(ab, ce_id, ie2_reg_addr); + ath12k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + ie3_reg_addr); + } +} + +static void ath12k_ahb_sync_ce_irqs(struct ath12k_base *ab) +{ + int i; + int irq_idx; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH12K_IRQ_CE0_OFFSET + i; + synchronize_irq(ab->irq_num[irq_idx]); + } +} + +static void ath12k_ahb_sync_ext_irqs(struct ath12k_base *ab) +{ + int i, j; + int irq_idx; + + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + synchronize_irq(ab->irq_num[irq_idx]); + } + } +} + +static void ath12k_ahb_ce_irqs_enable(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + ath12k_ahb_ce_irq_enable(ab, i); + } +} + +static void ath12k_ahb_ce_irqs_disable(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + ath12k_ahb_ce_irq_disable(ab, i); + } +} + +static int ath12k_ahb_start(struct ath12k_base *ab) +{ + ath12k_ahb_ce_irqs_enable(ab); + ath12k_ce_rx_post_buf(ab); + + return 0; +} + +static void ath12k_ahb_ext_irq_enable(struct ath12k_base *ab) +{ + struct ath12k_ext_irq_grp *irq_grp; + int i; + + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + irq_grp = &ab->ext_irq_grp[i]; + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } + ath12k_ahb_ext_grp_enable(irq_grp); + } +} + +static void ath12k_ahb_ext_irq_disable(struct ath12k_base *ab) +{ + __ath12k_ahb_ext_irq_disable(ab); + ath12k_ahb_sync_ext_irqs(ab); +} + +static void ath12k_ahb_stop(struct ath12k_base *ab) +{ + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath12k_ahb_ce_irqs_disable(ab); + ath12k_ahb_sync_ce_irqs(ab); + ath12k_ahb_cancel_workqueue(ab); + timer_delete_sync(&ab->rx_replenish_retry); + ath12k_ce_cleanup_pipes(ab); +} + +static int ath12k_ahb_power_up(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + char fw_name[ATH12K_USERPD_FW_NAME_LEN]; + char fw2_name[ATH12K_USERPD_FW_NAME_LEN]; + struct device *dev = ab->dev; + const struct firmware *fw, *fw2; + struct reserved_mem *rmem = NULL; + unsigned long time_left; + phys_addr_t mem_phys; + void *mem_region; + size_t mem_size; + u32 pasid; + int ret; + + rmem = ath12k_core_get_reserved_mem(ab, 0); + if (!rmem) + return -ENODEV; + + mem_phys = rmem->base; + mem_size = rmem->size; + mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC); + if (IS_ERR(mem_region)) { + ath12k_err(ab, "unable to map memory region: %pa+%pa\n", + &rmem->base, &rmem->size); + return PTR_ERR(mem_region); + } + + snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ab_ahb->userpd_id, + ATH12K_AHB_FW_SUFFIX); + + ret = request_firmware(&fw, fw_name, dev); + if (ret < 0) { + ath12k_err(ab, "request_firmware failed\n"); + return ret; + } + + ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw_name, + fw->size); + + if (!fw->size) { + ath12k_err(ab, "Invalid firmware size\n"); + ret = -EINVAL; + goto err_fw; + } + + pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | + ATH12K_AHB_UPD_SWID; + + /* Load FW image to a reserved memory location */ + ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, mem_phys, mem_size, + &mem_phys); + if (ret) { + ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); + goto err_fw; + } + + snprintf(fw2_name, sizeof(fw2_name), "%s/%s/%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, ATH12K_AHB_FW2); + + ret = request_firmware(&fw2, fw2_name, dev); + if (ret < 0) { + ath12k_err(ab, "request_firmware failed\n"); + goto err_fw; + } + + ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw2_name, + fw2->size); + + if (!fw2->size) { + ath12k_err(ab, "Invalid firmware size\n"); + ret = -EINVAL; + goto err_fw2; + } + + ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, pasid, mem_region, mem_phys, + mem_size, &mem_phys); + if (ret) { + ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); + goto err_fw2; + } + + /* Authenticate FW image using peripheral ID */ + ret = qcom_scm_pas_auth_and_reset(pasid); + if (ret) { + ath12k_err(ab, "failed to boot the remote processor %d\n", ret); + goto err_fw2; + } + + /* Instruct Q6 to spawn userPD thread */ + ret = qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), + BIT(ab_ahb->spawn_bit)); + if (ret) { + ath12k_err(ab, "Failed to update spawn state %d\n", ret); + goto err_fw2; + } + + time_left = wait_for_completion_timeout(&ab_ahb->userpd_spawned, + ATH12K_USERPD_SPAWN_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "UserPD spawn wait timed out\n"); + ret = -ETIMEDOUT; + goto err_fw2; + } + + time_left = wait_for_completion_timeout(&ab_ahb->userpd_ready, + ATH12K_USERPD_READY_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "UserPD ready wait timed out\n"); + ret = -ETIMEDOUT; + goto err_fw2; + } + + qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), 0); + + ath12k_dbg(ab, ATH12K_DBG_AHB, "UserPD%d is now UP\n", ab_ahb->userpd_id); + +err_fw2: + release_firmware(fw2); +err_fw: + release_firmware(fw); + return ret; +} + +static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + unsigned long time_left; + u32 pasid; + int ret; + + qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), + BIT(ab_ahb->stop_bit)); + + time_left = wait_for_completion_timeout(&ab_ahb->userpd_stopped, + ATH12K_USERPD_STOP_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "UserPD stop wait timed out\n"); + return; + } + + qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0); + + pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | + ATH12K_AHB_UPD_SWID; + /* Release the firmware */ + ret = qcom_scm_pas_shutdown(pasid); + if (ret) + ath12k_err(ab, "scm pas shutdown failed for userPD%d: %d\n", + ab_ahb->userpd_id, ret); +} + +static void ath12k_ahb_init_qmi_ce_config(struct ath12k_base *ab) +{ + struct ath12k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce_len = ab->hw_params->target_ce_count; + cfg->tgt_ce = ab->hw_params->target_ce_config; + cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len; + cfg->svc_to_ce_map = ab->hw_params->svc_to_ce_map; + ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id; +} + +static void ath12k_ahb_ce_workqueue(struct work_struct *work) +{ + struct ath12k_ce_pipe *ce_pipe = from_work(ce_pipe, work, intr_wq); + + ath12k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + + ath12k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); +} + +static irqreturn_t ath12k_ahb_ce_interrupt_handler(int irq, void *arg) +{ + struct ath12k_ce_pipe *ce_pipe = arg; + + /* last interrupt received for this CE */ + ce_pipe->timestamp = jiffies; + + ath12k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); + + queue_work(system_bh_wq, &ce_pipe->intr_wq); + + return IRQ_HANDLED; +} + +static int ath12k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget) +{ + struct ath12k_ext_irq_grp *irq_grp = container_of(napi, + struct ath12k_ext_irq_grp, + napi); + struct ath12k_base *ab = irq_grp->ab; + int work_done; + + work_done = ath12k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); + ath12k_ahb_ext_grp_enable(irq_grp); + } + + if (work_done > budget) + work_done = budget; + + return work_done; +} + +static irqreturn_t ath12k_ahb_ext_interrupt_handler(int irq, void *arg) +{ + struct ath12k_ext_irq_grp *irq_grp = arg; + + /* last interrupt received for this group */ + irq_grp->timestamp = jiffies; + + ath12k_ahb_ext_grp_disable(irq_grp); + + napi_schedule(&irq_grp->napi); + + return IRQ_HANDLED; +} + +static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) +{ + const struct ath12k_hw_ring_mask *ring_mask; + struct ath12k_ext_irq_grp *irq_grp; + const struct hal_ops *hal_ops; + int i, j, irq, irq_idx, ret; + u32 num_irq; + + ring_mask = ab->hw_params->ring_mask; + hal_ops = ab->hw_params->hal_ops; + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + irq_grp = &ab->ext_irq_grp[i]; + num_irq = 0; + + irq_grp->ab = ab; + irq_grp->grp_id = i; + + irq_grp->napi_ndev = alloc_netdev_dummy(0); + if (!irq_grp->napi_ndev) + return -ENOMEM; + + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, + ath12k_ahb_ext_grp_napi_poll); + + for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { + /* For TX ring, ensure that the ring mask and the + * tcl_to_wbm_rbm_map point to the same ring number. + */ + if (ring_mask->tx[i] & + BIT(hal_ops->tcl_to_wbm_rbm_map[j].wbm_ring_num)) { + irq_grp->irqs[num_irq++] = + wbm2host_tx_completions_ring1 - j; + } + + if (ring_mask->rx[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + reo2host_destination_ring1 - j; + } + + if (ring_mask->rx_err[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_exception; + + if (ring_mask->rx_wbm_rel[i] & BIT(j)) + irq_grp->irqs[num_irq++] = wbm2host_rx_release; + + if (ring_mask->reo_status[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_status; + + if (ring_mask->rx_mon_dest[i] & BIT(j)) + irq_grp->irqs[num_irq++] = + rxdma2host_monitor_destination_mac1; + } + + irq_grp->num_irq = num_irq; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + + irq = platform_get_irq_byname(ab->pdev, + irq_name[irq_idx]); + ab->irq_num[irq_idx] = irq; + irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY); + ret = devm_request_irq(ab->dev, irq, + ath12k_ahb_ext_interrupt_handler, + IRQF_TRIGGER_RISING, + irq_name[irq_idx], irq_grp); + if (ret) + ath12k_warn(ab, "failed request_irq for %d\n", irq); + } + } + + return 0; +} + +static int ath12k_ahb_config_irq(struct ath12k_base *ab) +{ + int irq, irq_idx, i; + int ret; + + /* Configure CE irqs */ + for (i = 0; i < ab->hw_params->ce_count; i++) { + struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH12K_IRQ_CE0_OFFSET + i; + + INIT_WORK(&ce_pipe->intr_wq, ath12k_ahb_ce_workqueue); + irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); + ret = devm_request_irq(ab->dev, irq, ath12k_ahb_ce_interrupt_handler, + IRQF_TRIGGER_RISING, irq_name[irq_idx], + ce_pipe); + if (ret) + return ret; + + ab->irq_num[irq_idx] = irq; + } + + /* Configure external interrupts */ + ret = ath12k_ahb_config_ext_irq(ab); + + return ret; +} + +static int ath12k_ahb_map_service_to_pipe(struct ath12k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + u32 pipedir; + int i; + + for (i = 0; i < ab->hw_params->svc_to_ce_map_len; i++) { + entry = &ab->hw_params->svc_to_ce_map[i]; + + if (__le32_to_cpu(entry->service_id) != service_id) + continue; + + pipedir = __le32_to_cpu(entry->pipedir); + if (pipedir == PIPEDIR_IN || pipedir == PIPEDIR_INOUT) { + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + } + + if (pipedir == PIPEDIR_OUT || pipedir == PIPEDIR_INOUT) { + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + } + } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + + return 0; +} + +static const struct ath12k_hif_ops ath12k_ahb_hif_ops_ipq5332 = { + .start = ath12k_ahb_start, + .stop = ath12k_ahb_stop, + .read32 = ath12k_ahb_read32, + .write32 = ath12k_ahb_write32, + .irq_enable = ath12k_ahb_ext_irq_enable, + .irq_disable = ath12k_ahb_ext_irq_disable, + .map_service_to_pipe = ath12k_ahb_map_service_to_pipe, + .power_up = ath12k_ahb_power_up, + .power_down = ath12k_ahb_power_down, +}; + +static irqreturn_t ath12k_userpd_irq_handler(int irq, void *data) +{ + struct ath12k_base *ab = data; + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_SPAWN_IRQ]) { + complete(&ab_ahb->userpd_spawned); + } else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_READY_IRQ]) { + complete(&ab_ahb->userpd_ready); + } else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_STOP_ACK_IRQ]) { + complete(&ab_ahb->userpd_stopped); + } else { + ath12k_err(ab, "Invalid userpd interrupt\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int ath12k_ahb_config_rproc_irq(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + int i, ret; + char *upd_irq_name; + + for (i = 0; i < ATH12K_USERPD_MAX_IRQ; i++) { + ab_ahb->userpd_irq_num[i] = platform_get_irq_byname(ab->pdev, + ath12k_userpd_irq[i]); + if (ab_ahb->userpd_irq_num[i] < 0) + return ab_ahb->userpd_irq_num[i]; + + upd_irq_name = devm_kzalloc(&ab->pdev->dev, ATH12K_UPD_IRQ_WRD_LEN, + GFP_KERNEL); + if (!upd_irq_name) + return -ENOMEM; + + scnprintf(upd_irq_name, ATH12K_UPD_IRQ_WRD_LEN, "UserPD%u-%s", + ab_ahb->userpd_id, ath12k_userpd_irq[i]); + ret = devm_request_threaded_irq(&ab->pdev->dev, ab_ahb->userpd_irq_num[i], + NULL, ath12k_userpd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + upd_irq_name, ab); + if (ret) + return dev_err_probe(&ab->pdev->dev, ret, + "Request %s irq failed: %d\n", + ath12k_userpd_irq[i], ret); + } + + ab_ahb->spawn_state = devm_qcom_smem_state_get(&ab->pdev->dev, "spawn", + &ab_ahb->spawn_bit); + if (IS_ERR(ab_ahb->spawn_state)) + return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->spawn_state), + "Failed to acquire spawn state\n"); + + ab_ahb->stop_state = devm_qcom_smem_state_get(&ab->pdev->dev, "stop", + &ab_ahb->stop_bit); + if (IS_ERR(ab_ahb->stop_state)) + return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->stop_state), + "Failed to acquire stop state\n"); + + init_completion(&ab_ahb->userpd_spawned); + init_completion(&ab_ahb->userpd_ready); + init_completion(&ab_ahb->userpd_stopped); + return 0; +} + +static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb, + const unsigned long event, void *data) +{ + struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb); + struct ath12k_base *ab = ab_ahb->ab; + + if (event == ATH12K_RPROC_AFTER_POWERUP) { + ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n"); + complete(&ab_ahb->rootpd_ready); + } + + return 0; +} + +static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier; + init_completion(&ab_ahb->rootpd_ready); + + ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name, + &ab_ahb->root_pd_nb); + if (IS_ERR(ab_ahb->root_pd_notifier)) + return PTR_ERR(ab_ahb->root_pd_notifier); + + return 0; +} + +static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + if (!ab_ahb->root_pd_notifier) { + ath12k_err(ab, "Rproc notifier not registered\n"); + return; + } + + qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier, + &ab_ahb->root_pd_nb); + ab_ahb->root_pd_notifier = NULL; +} + +static int ath12k_ahb_get_rproc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct device *dev = ab->dev; + struct device_node *np; + struct rproc *prproc; + + np = of_parse_phandle(dev->of_node, "qcom,rproc", 0); + if (!np) { + ath12k_err(ab, "failed to get q6_rproc handle\n"); + return -ENOENT; + } + + prproc = rproc_get_by_phandle(np->phandle); + of_node_put(np); + if (!prproc) + return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER, + "failed to get rproc\n"); + + ab_ahb->tgt_rproc = prproc; + + return 0; +} + +static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + unsigned long time_left; + int ret; + + ret = rproc_boot(ab_ahb->tgt_rproc); + if (ret < 0) { + ath12k_err(ab, "RootPD boot failed\n"); + return ret; + } + + time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready, + ATH12K_ROOTPD_READY_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "RootPD ready wait timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ath12k_ahb_configure_rproc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + int ret; + + ret = ath12k_ahb_get_rproc(ab); + if (ret < 0) + return ret; + + ret = ath12k_ahb_register_rproc_notifier(ab); + if (ret < 0) { + ret = dev_err_probe(&ab->pdev->dev, ret, + "failed to register rproc notifier\n"); + goto err_put_rproc; + } + + if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) { + ret = ath12k_ahb_boot_root_pd(ab); + if (ret < 0) { + ath12k_err(ab, "failed to boot the remote processor Q6\n"); + goto err_unreg_notifier; + } + } + + return ath12k_ahb_config_rproc_irq(ab); + +err_unreg_notifier: + ath12k_ahb_unregister_rproc_notifier(ab); + +err_put_rproc: + rproc_put(ab_ahb->tgt_rproc); + return ret; +} + +static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + ath12k_ahb_unregister_rproc_notifier(ab); + rproc_put(ab_ahb->tgt_rproc); +} + +static int ath12k_ahb_resource_init(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct platform_device *pdev = ab->pdev; + struct resource *mem_res; + int ret; + + ab->mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); + if (IS_ERR(ab->mem)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(ab->mem), "ioremap error\n"); + goto out; + } + + ab->mem_len = resource_size(mem_res); + + if (ab->hw_params->ce_remap) { + const struct ce_remap *ce_remap = ab->hw_params->ce_remap; + /* CE register space is moved out of WCSS and the space is not + * contiguous, hence remapping the CE registers to a new space + * for accessing them. + */ + ab->mem_ce = ioremap(ce_remap->base, ce_remap->size); + if (!ab->mem_ce) { + dev_err(&pdev->dev, "ce ioremap error\n"); + ret = -ENOMEM; + goto err_mem_unmap; + } + ab->ce_remap = true; + ab->ce_remap_base_addr = HAL_IPQ5332_CE_WFSS_REG_BASE; + } + + ab_ahb->xo_clk = devm_clk_get(ab->dev, "xo"); + if (IS_ERR(ab_ahb->xo_clk)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(ab_ahb->xo_clk), + "failed to get xo clock\n"); + goto err_mem_ce_unmap; + } + + ret = clk_prepare_enable(ab_ahb->xo_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable gcc_xo_clk: %d\n", ret); + goto err_clock_deinit; + } + + return 0; + +err_clock_deinit: + devm_clk_put(ab->dev, ab_ahb->xo_clk); + +err_mem_ce_unmap: + ab_ahb->xo_clk = NULL; + if (ab->hw_params->ce_remap) + iounmap(ab->mem_ce); + +err_mem_unmap: + ab->mem_ce = NULL; + devm_iounmap(ab->dev, ab->mem); + +out: + ab->mem = NULL; + return ret; +} + +static void ath12k_ahb_resource_deinit(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + if (ab->mem) + devm_iounmap(ab->dev, ab->mem); + + if (ab->mem_ce) + iounmap(ab->mem_ce); + + ab->mem = NULL; + ab->mem_ce = NULL; + + clk_disable_unprepare(ab_ahb->xo_clk); + devm_clk_put(ab->dev, ab_ahb->xo_clk); + ab_ahb->xo_clk = NULL; +} + +static int ath12k_ahb_probe(struct platform_device *pdev) +{ + struct ath12k_base *ab; + const struct ath12k_hif_ops *hif_ops; + struct ath12k_ahb *ab_ahb; + enum ath12k_hw_rev hw_rev; + u32 addr, userpd_id; + int ret; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set 32-bit coherent dma\n"); + return ret; + } + + ab = ath12k_core_alloc(&pdev->dev, sizeof(struct ath12k_ahb), + ATH12K_BUS_AHB); + if (!ab) + return -ENOMEM; + + hw_rev = (enum ath12k_hw_rev)(kernel_ulong_t)of_device_get_match_data(&pdev->dev); + switch (hw_rev) { + case ATH12K_HW_IPQ5332_HW10: + hif_ops = &ath12k_ahb_hif_ops_ipq5332; + userpd_id = ATH12K_IPQ5332_USERPD_ID; + break; + default: + ret = -EOPNOTSUPP; + goto err_core_free; + } + + ab->hif.ops = hif_ops; + ab->pdev = pdev; + ab->hw_rev = hw_rev; + platform_set_drvdata(pdev, ab); + ab_ahb = ath12k_ab_to_ahb(ab); + ab_ahb->ab = ab; + ab_ahb->userpd_id = userpd_id; + + /* Set fixed_mem_region to true for platforms that support fixed memory + * reservation from DT. If memory is reserved from DT for FW, ath12k driver + * need not to allocate memory. + */ + if (!of_property_read_u32(ab->dev->of_node, "memory-region", &addr)) + set_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags); + + ret = ath12k_core_pre_init(ab); + if (ret) + goto err_core_free; + + ret = ath12k_ahb_resource_init(ab); + if (ret) + goto err_core_free; + + ret = ath12k_hal_srng_init(ab); + if (ret) + goto err_resource_deinit; + + ret = ath12k_ce_alloc_pipes(ab); + if (ret) { + ath12k_err(ab, "failed to allocate ce pipes: %d\n", ret); + goto err_hal_srng_deinit; + } + + ath12k_ahb_init_qmi_ce_config(ab); + + ret = ath12k_ahb_configure_rproc(ab); + if (ret) + goto err_ce_free; + + ret = ath12k_ahb_config_irq(ab); + if (ret) { + ath12k_err(ab, "failed to configure irq: %d\n", ret); + goto err_rproc_deconfigure; + } + + ret = ath12k_core_init(ab); + if (ret) { + ath12k_err(ab, "failed to init core: %d\n", ret); + goto err_rproc_deconfigure; + } + + return 0; + +err_rproc_deconfigure: + ath12k_ahb_deconfigure_rproc(ab); + +err_ce_free: + ath12k_ce_free_pipes(ab); + +err_hal_srng_deinit: + ath12k_hal_srng_deinit(ab); + +err_resource_deinit: + ath12k_ahb_resource_deinit(ab); + +err_core_free: + ath12k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static void ath12k_ahb_remove_prepare(struct ath12k_base *ab) +{ + unsigned long left; + + if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) { + left = wait_for_completion_timeout(&ab->driver_recovery, + ATH12K_AHB_RECOVERY_TIMEOUT); + if (!left) + ath12k_warn(ab, "failed to receive recovery response completion\n"); + } + + set_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->restart_work); + cancel_work_sync(&ab->qmi.event_work); +} + +static void ath12k_ahb_free_resources(struct ath12k_base *ab) +{ + struct platform_device *pdev = ab->pdev; + + ath12k_hal_srng_deinit(ab); + ath12k_ce_free_pipes(ab); + ath12k_ahb_resource_deinit(ab); + ath12k_ahb_deconfigure_rproc(ab); + ath12k_core_free(ab); + platform_set_drvdata(pdev, NULL); +} + +static void ath12k_ahb_remove(struct platform_device *pdev) +{ + struct ath12k_base *ab = platform_get_drvdata(pdev); + + if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath12k_ahb_power_down(ab, false); + goto qmi_fail; + } + + ath12k_ahb_remove_prepare(ab); + ath12k_core_hw_group_cleanup(ab->ag); +qmi_fail: + ath12k_core_deinit(ab); + ath12k_ahb_free_resources(ab); +} + +static struct platform_driver ath12k_ahb_driver = { + .driver = { + .name = "ath12k_ahb", + .of_match_table = ath12k_ahb_of_match, + }, + .probe = ath12k_ahb_probe, + .remove = ath12k_ahb_remove, +}; + +int ath12k_ahb_init(void) +{ + return platform_driver_register(&ath12k_ahb_driver); +} + +void ath12k_ahb_exit(void) +{ + platform_driver_unregister(&ath12k_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h new file mode 100644 index 000000000000..d56244b20a6a --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef ATH12K_AHB_H +#define ATH12K_AHB_H + +#include <linux/clk.h> +#include <linux/remoteproc/qcom_rproc.h> +#include "core.h" + +#define ATH12K_AHB_RECOVERY_TIMEOUT (3 * HZ) + +#define ATH12K_AHB_SMP2P_SMEM_MSG GENMASK(15, 0) +#define ATH12K_AHB_SMP2P_SMEM_SEQ_NO GENMASK(31, 16) +#define ATH12K_AHB_SMP2P_SMEM_VALUE_MASK 0xFFFFFFFF +#define ATH12K_PCI_CE_WAKE_IRQ 2 +#define ATH12K_PCI_IRQ_CE0_OFFSET 3 +#define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ) +#define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP +#define ATH12K_AHB_FW_PREFIX "q6_fw" +#define ATH12K_AHB_FW_SUFFIX ".mdt" +#define ATH12K_AHB_FW2 "iu_fw.mdt" +#define ATH12K_AHB_UPD_SWID 0x12 +#define ATH12K_USERPD_SPAWN_TIMEOUT (5 * HZ) +#define ATH12K_USERPD_READY_TIMEOUT (10 * HZ) +#define ATH12K_USERPD_STOP_TIMEOUT (5 * HZ) +#define ATH12K_USERPD_ID_MASK GENMASK(9, 8) +#define ATH12K_USERPD_FW_NAME_LEN 35 + +enum ath12k_ahb_smp2p_msg_id { + ATH12K_AHB_POWER_SAVE_ENTER = 1, + ATH12K_AHB_POWER_SAVE_EXIT, +}; + +enum ath12k_ahb_userpd_irq { + ATH12K_USERPD_SPAWN_IRQ, + ATH12K_USERPD_READY_IRQ, + ATH12K_USERPD_STOP_ACK_IRQ, + ATH12K_USERPD_MAX_IRQ, +}; + +struct ath12k_base; + +struct ath12k_ahb { + struct ath12k_base *ab; + struct rproc *tgt_rproc; + struct clk *xo_clk; + struct completion rootpd_ready; + struct notifier_block root_pd_nb; + void *root_pd_notifier; + struct qcom_smem_state *spawn_state; + struct qcom_smem_state *stop_state; + struct completion userpd_spawned; + struct completion userpd_ready; + struct completion userpd_stopped; + u32 userpd_id; + u32 spawn_bit; + u32 stop_bit; + int userpd_irq_num[ATH12K_USERPD_MAX_IRQ]; +}; + +static inline struct ath12k_ahb *ath12k_ab_to_ahb(struct ath12k_base *ab) +{ + return (struct ath12k_ahb *)ab->drv_priv; +} + +#ifdef CONFIG_ATH12K_AHB +int ath12k_ahb_init(void); +void ath12k_ahb_exit(void); +#else +static inline int ath12k_ahb_init(void) +{ + return 0; +} + +static inline void ath12k_ahb_exit(void) {}; +#endif +#endif diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c index be0d669d31fc..3f3439262cf4 100644 --- a/drivers/net/wireless/ath/ath12k/ce.c +++ b/drivers/net/wireless/ath/ath12k/ce.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "dp_rx.h" @@ -219,6 +219,96 @@ const struct ce_attr ath12k_host_ce_config_wcn7850[] = { }; +const struct ce_attr ath12k_host_ce_config_ipq5332[] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath12k_htc_rx_completion_handler, + }, + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 128, + .recv_cb = ath12k_htc_rx_completion_handler, + }, + /* CE3: host->target WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + /* CE5: target -> host PKTLOG */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath12k_dp_htt_htc_t2h_msg_handler, + }, + /* CE6: Target autonomous HIF_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE7: CV Prefetch */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE8: Target HIF memcpy (Generic HIF memcypy) */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE9: WMI logging/CFR/Spectral/Radar */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 128, + }, + /* CE10: Unused */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE11: Unused */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, +}; + static int ath12k_ce_rx_buf_enqueue_pipe(struct ath12k_ce_pipe *pipe, struct sk_buff *skb, dma_addr_t paddr) { @@ -343,11 +433,10 @@ static int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe, goto err; } + /* Make sure descriptor is read after the head pointer. */ + dma_rmb(); + *nbytes = ath12k_hal_ce_dst_status_get_length(desc); - if (*nbytes == 0) { - ret = -EIO; - goto err; - } *skb = pipe->dest_ring->skb[sw_index]; pipe->dest_ring->skb[sw_index] = NULL; @@ -380,8 +469,8 @@ static void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe) dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); - if (unlikely(max_nbytes < nbytes)) { - ath12k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + if (unlikely(max_nbytes < nbytes || nbytes == 0)) { + ath12k_warn(ab, "unexpected rx length (nbytes %d, max %d)", nbytes, max_nbytes); dev_kfree_skb_any(skb); continue; @@ -779,7 +868,7 @@ void ath12k_ce_rx_post_buf(struct ath12k_base *ab) void ath12k_ce_rx_replenish_retry(struct timer_list *t) { - struct ath12k_base *ab = from_timer(ab, t, rx_replenish_retry); + struct ath12k_base *ab = timer_container_of(ab, t, rx_replenish_retry); ath12k_ce_rx_post_buf(ab); } diff --git a/drivers/net/wireless/ath/ath12k/ce.h b/drivers/net/wireless/ath/ath12k/ce.h index 79af3b6159f1..57f75899ee03 100644 --- a/drivers/net/wireless/ath/ath12k/ce.h +++ b/drivers/net/wireless/ath/ath12k/ce.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_CE_H @@ -39,8 +39,8 @@ #define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ /* CE address/mask */ -#define CE_HOST_IE_ADDRESS 0x00A1803C -#define CE_HOST_IE_2_ADDRESS 0x00A18040 +#define CE_HOST_IE_ADDRESS 0x75804C +#define CE_HOST_IE_2_ADDRESS 0x758050 #define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS #define CE_HOST_IE_3_SHIFT 0xC @@ -76,6 +76,17 @@ struct ce_pipe_config { __le32 reserved; }; +struct ce_ie_addr { + u32 ie1_reg_addr; + u32 ie2_reg_addr; + u32 ie3_reg_addr; +}; + +struct ce_remap { + u32 base; + u32 size; +}; + struct ce_attr { /* CE_ATTR_* values */ unsigned int flags; @@ -119,7 +130,7 @@ struct ath12k_ce_ring { /* Host address space */ void *base_addr_owner_space_unaligned; /* CE address space */ - u32 base_addr_ce_space_unaligned; + dma_addr_t base_addr_ce_space_unaligned; /* Actual start of descriptors. * Aligned to descriptor-size boundary. @@ -129,7 +140,7 @@ struct ath12k_ce_ring { void *base_addr_owner_space; /* CE address space */ - u32 base_addr_ce_space; + dma_addr_t base_addr_ce_space; /* HAL ring id */ u32 hal_ring_id; @@ -148,7 +159,7 @@ struct ath12k_ce_pipe { void (*send_cb)(struct ath12k_ce_pipe *pipe); void (*recv_cb)(struct ath12k_base *ab, struct sk_buff *skb); - struct tasklet_struct intr_tq; + struct work_struct intr_wq; struct ath12k_ce_ring *src_ring; struct ath12k_ce_ring *dest_ring; struct ath12k_ce_ring *status_ring; @@ -164,6 +175,7 @@ struct ath12k_ce { extern const struct ce_attr ath12k_host_ce_config_qcn9274[]; extern const struct ce_attr ath12k_host_ce_config_wcn7850[]; +extern const struct ce_attr ath12k_host_ce_config_ipq5332[]; void ath12k_ce_cleanup_pipes(struct ath12k_base *ab); void ath12k_ce_rx_replenish_retry(struct timer_list *t); diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 391b6fb2bd42..89ae80934b30 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -9,17 +9,32 @@ #include <linux/remoteproc.h> #include <linux/firmware.h> #include <linux/of.h> +#include <linux/of_graph.h> +#include "ahb.h" #include "core.h" #include "dp_tx.h" #include "dp_rx.h" #include "debug.h" -#include "hif.h" +#include "debugfs.h" #include "fw.h" +#include "hif.h" +#include "pci.h" +#include "wow.h" +static int ahb_err, pci_err; unsigned int ath12k_debug_mask; module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); +bool ath12k_ftm_mode; +module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444); +MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode"); + +/* protected with ath12k_hw_group_mutex */ +static struct list_head ath12k_hw_group_list = LIST_HEAD_INIT(ath12k_hw_group_list); + +static DEFINE_MUTEX(ath12k_hw_group_mutex); + static int ath12k_core_rfkill_config(struct ath12k_base *ab) { struct ath12k *ar; @@ -28,6 +43,9 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL)) return 0; + if (ath12k_acpi_get_disable_rfkill(ab)) + return 0; + for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -41,73 +59,131 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) return ret; } -int ath12k_core_suspend(struct ath12k_base *ab) +/* Check if we need to continue with suspend/resume operation. + * Return: + * a negative value: error happens and don't continue. + * 0: no error but don't continue. + * positive value: no error and do continue. + */ +static int ath12k_core_continue_suspend_resume(struct ath12k_base *ab) { - int ret; + struct ath12k *ar; if (!ab->hw_params->supports_suspend) return -EOPNOTSUPP; - /* TODO: there can frames in queues so for now add delay as a hack. - * Need to implement to handle and remove this delay. + /* so far single_pdev_only chips have supports_suspend as true + * so pass 0 as a dummy pdev_id here. */ - msleep(500); + ar = ab->pdevs[0].ar; + if (!ar || !ar->ah || ar->ah->state != ATH12K_HW_STATE_OFF) + return 0; - ret = ath12k_dp_rx_pktlog_stop(ab, true); - if (ret) { - ath12k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n", - ret); + return 1; +} + +int ath12k_core_suspend(struct ath12k_base *ab) +{ + struct ath12k *ar; + int ret, i; + + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) return ret; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + if (!ar) + continue; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + + ret = ath12k_mac_wait_tx_complete(ar); + if (ret) { + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + ath12k_warn(ab, "failed to wait tx complete: %d\n", ret); + return ret; + } + + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); } - ret = ath12k_dp_rx_pktlog_stop(ab, false); - if (ret) { - ath12k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n", - ret); + /* PM framework skips suspend_late/resume_early callbacks + * if other devices report errors in their suspend callbacks. + * However ath12k_core_resume() would still be called because + * here we return success thus kernel put us on dpm_suspended_list. + * Since we won't go through a power down/up cycle, there is + * no chance to call complete(&ab->restart_completed) in + * ath12k_core_restart(), making ath12k_core_resume() timeout. + * So call it here to avoid this issue. This also works in case + * no error happens thus suspend_late/resume_early get called, + * because it will be reinitialized in ath12k_core_resume_early(). + */ + complete(&ab->restart_completed); + + return 0; +} +EXPORT_SYMBOL(ath12k_core_suspend); + +int ath12k_core_suspend_late(struct ath12k_base *ab) +{ + int ret; + + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) return ret; - } + + ath12k_acpi_stop(ab); ath12k_hif_irq_disable(ab); ath12k_hif_ce_irq_disable(ab); - ret = ath12k_hif_suspend(ab); - if (ret) { - ath12k_warn(ab, "failed to suspend hif: %d\n", ret); - return ret; - } + ath12k_hif_power_down(ab, true); return 0; } +EXPORT_SYMBOL(ath12k_core_suspend_late); -int ath12k_core_resume(struct ath12k_base *ab) +int ath12k_core_resume_early(struct ath12k_base *ab) { int ret; - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; - - ret = ath12k_hif_resume(ab); - if (ret) { - ath12k_warn(ab, "failed to resume hif during resume: %d\n", ret); + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) return ret; - } - ath12k_hif_ce_irq_enable(ab); - ath12k_hif_irq_enable(ab); + reinit_completion(&ab->restart_completed); + ret = ath12k_hif_power_up(ab); + if (ret) + ath12k_warn(ab, "failed to power up hif during resume: %d\n", ret); - ret = ath12k_dp_rx_pktlog_start(ab); - if (ret) { - ath12k_warn(ab, "failed to start rx pktlog during resume: %d\n", - ret); + return ret; +} +EXPORT_SYMBOL(ath12k_core_resume_early); + +int ath12k_core_resume(struct ath12k_base *ab) +{ + long time_left; + int ret; + + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) return ret; + + time_left = wait_for_completion_timeout(&ab->restart_completed, + ATH12K_RESET_TIMEOUT_HZ); + if (time_left == 0) { + ath12k_warn(ab, "timeout while waiting for restart complete"); + return -ETIMEDOUT; } return 0; } +EXPORT_SYMBOL(ath12k_core_resume); static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len, bool with_variant, - bool bus_type_mode) + bool bus_type_mode, bool with_default) { /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; @@ -138,7 +214,9 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", ath12k_bus_str(ab->hif.bus), ab->qmi.target.chip_id, - ab->qmi.target.board_id, variant); + with_default ? + ATH12K_BOARD_ID_DEFAULT : ab->qmi.target.board_id, + variant); break; } @@ -150,19 +228,19 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, true, false); + return __ath12k_core_create_board_name(ab, name, name_len, true, false, false); } static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false, false); + return __ath12k_core_create_board_name(ab, name, name_len, false, false, true); } static int ath12k_core_create_bus_type_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false, true); + return __ath12k_core_create_board_name(ab, name, name_len, false, true, true); } const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, @@ -537,19 +615,89 @@ u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab) return TARGET_NUM_TIDS(SINGLE); } +struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab, + int index) +{ + struct device *dev = ab->dev; + struct reserved_mem *rmem; + struct device_node *node; + + node = of_parse_phandle(dev->of_node, "memory-region", index); + if (!node) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "failed to parse memory-region for index %d\n", index); + return NULL; + } + + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "unable to get memory-region for index %d\n", index); + return NULL; + } + + return rmem; +} + +static inline +void ath12k_core_to_group_ref_get(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + + lockdep_assert_held(&ag->mutex); + + if (ab->hw_group_ref) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already attached to group %d\n", + ag->id); + return; + } + + ab->hw_group_ref = true; + ag->num_started++; + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core attached to group %d, num_started %d\n", + ag->id, ag->num_started); +} + +static inline +void ath12k_core_to_group_ref_put(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + + lockdep_assert_held(&ag->mutex); + + if (!ab->hw_group_ref) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already de-attached from group %d\n", + ag->id); + return; + } + + ab->hw_group_ref = false; + ag->num_started--; + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core de-attached from group %d, num_started %d\n", + ag->id, ag->num_started); +} + static void ath12k_core_stop(struct ath12k_base *ab) { + ath12k_core_to_group_ref_put(ab); + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) ath12k_qmi_firmware_stop(ab); + ath12k_acpi_stop(ab); + + ath12k_dp_rx_pdev_reo_cleanup(ab); ath12k_hif_stop(ab); ath12k_wmi_detach(ab); - ath12k_dp_rx_pdev_reo_cleanup(ab); + ath12k_dp_free(ab); /* De-Init of components as needed */ } -static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) +static void ath12k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data) { struct ath12k_base *ab = data; const char *magic = ATH12K_SMBIOS_BDF_EXT_MAGIC; @@ -571,6 +719,28 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) return; } + spin_lock_bh(&ab->base_lock); + + switch (smbios->country_code_flag) { + case ATH12K_SMBIOS_CC_ISO: + ab->new_alpha2[0] = u16_get_bits(smbios->cc_code >> 8, 0xff); + ab->new_alpha2[1] = u16_get_bits(smbios->cc_code, 0xff); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios cc_code %c%c\n", + ab->new_alpha2[0], ab->new_alpha2[1]); + break; + case ATH12K_SMBIOS_CC_WW: + ab->new_alpha2[0] = '0'; + ab->new_alpha2[1] = '0'; + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios worldwide regdomain\n"); + break; + default: + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot ignore smbios country code setting %d\n", + smbios->country_code_flag); + break; + } + + spin_unlock_bh(&ab->base_lock); + if (!smbios->bdf_enabled) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "bdf variant name not found.\n"); return; @@ -610,7 +780,7 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) int ath12k_core_check_smbios(struct ath12k_base *ab) { ab->qmi.target.bdf_ext[0] = '\0'; - dmi_walk(ath12k_core_check_bdfext, ab); + dmi_walk(ath12k_core_check_cc_code_bdfext, ab); if (ab->qmi.target.bdf_ext[0] == '\0') return -ENODATA; @@ -622,29 +792,40 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) { int ret; + if (ath12k_ftm_mode) { + ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM; + ath12k_info(ab, "Booting in ftm mode\n"); + } + ret = ath12k_qmi_init_service(ab); if (ret) { ath12k_err(ab, "failed to initialize qmi :%d\n", ret); return ret; } + ath12k_debugfs_soc_create(ab); + ret = ath12k_hif_power_up(ab); if (ret) { ath12k_err(ab, "failed to power up :%d\n", ret); goto err_qmi_deinit; } + ath12k_debugfs_pdev_create(ab); + return 0; err_qmi_deinit: + ath12k_debugfs_soc_destroy(ab); ath12k_qmi_deinit_service(ab); return ret; } static void ath12k_core_soc_destroy(struct ath12k_base *ab) { - ath12k_dp_free(ab); + ath12k_hif_power_down(ab, false); ath12k_reg_free(ab); + ath12k_debugfs_soc_destroy(ab); ath12k_qmi_deinit_service(ab); } @@ -652,38 +833,26 @@ static int ath12k_core_pdev_create(struct ath12k_base *ab) { int ret; - ret = ath12k_mac_register(ab); - if (ret) { - ath12k_err(ab, "failed register the radio with mac80211: %d\n", ret); - return ret; - } - ret = ath12k_dp_pdev_alloc(ab); if (ret) { ath12k_err(ab, "failed to attach DP pdev: %d\n", ret); - goto err_mac_unregister; + return ret; } return 0; - -err_mac_unregister: - ath12k_mac_unregister(ab); - - return ret; } static void ath12k_core_pdev_destroy(struct ath12k_base *ab) { - ath12k_mac_unregister(ab); - ath12k_hif_irq_disable(ab); ath12k_dp_pdev_free(ab); } -static int ath12k_core_start(struct ath12k_base *ab, - enum ath12k_firmware_mode mode) +static int ath12k_core_start(struct ath12k_base *ab) { int ret; + lockdep_assert_held(&ab->core_lock); + ret = ath12k_wmi_attach(ab); if (ret) { ath12k_err(ab, "failed to attach wmi: %d\n", ret); @@ -733,19 +902,12 @@ static int ath12k_core_start(struct ath12k_base *ab, goto err_hif_stop; } - ret = ath12k_mac_allocate(ab); - if (ret) { - ath12k_err(ab, "failed to create new hw device with mac80211 :%d\n", - ret); - goto err_hif_stop; - } - ath12k_dp_cc_config(ab); ret = ath12k_dp_rx_pdev_reo_setup(ab); if (ret) { ath12k_err(ab, "failed to initialize reo destination rings: %d\n", ret); - goto err_mac_destroy; + goto err_hif_stop; } ath12k_dp_hal_rx_desc_init(ab); @@ -779,12 +941,15 @@ static int ath12k_core_start(struct ath12k_base *ab, goto err_reo_cleanup; } + ath12k_acpi_set_dsm_func(ab); + + /* Indicate the core start in the appropriate group */ + ath12k_core_to_group_ref_get(ab); + return 0; err_reo_cleanup: ath12k_dp_rx_pdev_reo_cleanup(ab); -err_mac_destroy: - ath12k_mac_destroy(ab); err_hif_stop: ath12k_hif_stop(ab); err_wmi_detach: @@ -792,6 +957,204 @@ err_wmi_detach: return ret; } +static void ath12k_core_device_cleanup(struct ath12k_base *ab) +{ + mutex_lock(&ab->core_lock); + + ath12k_hif_irq_disable(ab); + ath12k_core_pdev_destroy(ab); + + mutex_unlock(&ab->core_lock); +} + +static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + lockdep_assert_held(&ag->mutex); + + clear_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags); + + ath12k_mac_unregister(ag); + + for (i = ag->num_devices - 1; i >= 0; i--) { + ab = ag->ab[i]; + if (!ab) + continue; + + clear_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); + + ath12k_core_device_cleanup(ab); + } + + ath12k_mac_destroy(ag); +} + +u8 ath12k_get_num_partner_link(struct ath12k *ar) +{ + struct ath12k_base *partner_ab, *ab = ar->ab; + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_pdev *pdev; + u8 num_link = 0; + int i, j; + + lockdep_assert_held(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + partner_ab = ag->ab[i]; + + for (j = 0; j < partner_ab->num_radios; j++) { + pdev = &partner_ab->pdevs[j]; + + /* Avoid the self link */ + if (ar == pdev->ar) + continue; + + num_link++; + } + } + + return num_link; +} + +static int __ath12k_mac_mlo_ready(struct ath12k *ar) +{ + u8 num_link = ath12k_get_num_partner_link(ar); + int ret; + + if (num_link == 0) + return 0; + + ret = ath12k_wmi_mlo_ready(ar); + if (ret) { + ath12k_err(ar->ab, "MLO ready failed for pdev %d: %d\n", + ar->pdev_idx, ret); + return ret; + } + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mlo ready done for pdev %d\n", + ar->pdev_idx); + + return 0; +} + +int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag) +{ + struct ath12k_hw *ah; + struct ath12k *ar; + int ret; + int i, j; + + for (i = 0; i < ag->num_hw; i++) { + ah = ag->ah[i]; + if (!ah) + continue; + + for_each_ar(ah, ar, j) { + ar = &ah->radio[j]; + ret = __ath12k_mac_mlo_ready(ar); + if (ret) + return ret; + } + } + + return 0; +} + +static int ath12k_core_mlo_setup(struct ath12k_hw_group *ag) +{ + int ret, i; + + if (!ag->mlo_capable) + return 0; + + ret = ath12k_mac_mlo_setup(ag); + if (ret) + return ret; + + for (i = 0; i < ag->num_devices; i++) + ath12k_dp_partner_cc_init(ag->ab[i]); + + ret = ath12k_mac_mlo_ready(ag); + if (ret) + goto err_mlo_teardown; + + return 0; + +err_mlo_teardown: + ath12k_mac_mlo_teardown(ag); + + return ret; +} + +static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int ret, i; + + lockdep_assert_held(&ag->mutex); + + if (test_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags)) + goto core_pdev_create; + + ret = ath12k_mac_allocate(ag); + if (WARN_ON(ret)) + return ret; + + ret = ath12k_core_mlo_setup(ag); + if (WARN_ON(ret)) + goto err_mac_destroy; + + ret = ath12k_mac_register(ag); + if (WARN_ON(ret)) + goto err_mlo_teardown; + + set_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags); + +core_pdev_create: + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + mutex_lock(&ab->core_lock); + + set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); + + ret = ath12k_core_pdev_create(ab); + if (ret) { + ath12k_err(ab, "failed to create pdev core %d\n", ret); + mutex_unlock(&ab->core_lock); + goto err; + } + + ath12k_hif_irq_enable(ab); + + ret = ath12k_core_rfkill_config(ab); + if (ret && ret != -EOPNOTSUPP) { + mutex_unlock(&ab->core_lock); + goto err; + } + + mutex_unlock(&ab->core_lock); + } + + return 0; + +err: + ath12k_core_hw_group_stop(ag); + return ret; + +err_mlo_teardown: + ath12k_mac_mlo_teardown(ag); + +err_mac_destroy: + ath12k_mac_destroy(ag); + + return ret; +} + static int ath12k_core_start_firmware(struct ath12k_base *ab, enum ath12k_firmware_mode mode) { @@ -809,11 +1172,94 @@ static int ath12k_core_start_firmware(struct ath12k_base *ab, return ret; } +static inline +bool ath12k_core_hw_group_start_ready(struct ath12k_hw_group *ag) +{ + lockdep_assert_held(&ag->mutex); + + return (ag->num_started == ag->num_devices); +} + +static void ath12k_fw_stats_pdevs_free(struct list_head *head) +{ + struct ath12k_fw_stats_pdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +void ath12k_fw_stats_bcn_free(struct list_head *head) +{ + struct ath12k_fw_stats_bcn *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath12k_fw_stats_vdevs_free(struct list_head *head) +{ + struct ath12k_fw_stats_vdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +void ath12k_fw_stats_init(struct ath12k *ar) +{ + INIT_LIST_HEAD(&ar->fw_stats.vdevs); + INIT_LIST_HEAD(&ar->fw_stats.pdevs); + INIT_LIST_HEAD(&ar->fw_stats.bcn); + init_completion(&ar->fw_stats_complete); + init_completion(&ar->fw_stats_done); +} + +void ath12k_fw_stats_free(struct ath12k_fw_stats *stats) +{ + ath12k_fw_stats_pdevs_free(&stats->pdevs); + ath12k_fw_stats_vdevs_free(&stats->vdevs); + ath12k_fw_stats_bcn_free(&stats->bcn); +} + +void ath12k_fw_stats_reset(struct ath12k *ar) +{ + spin_lock_bh(&ar->data_lock); + ath12k_fw_stats_free(&ar->fw_stats); + ar->fw_stats.num_vdev_recvd = 0; + ar->fw_stats.num_bcn_recvd = 0; + spin_unlock_bh(&ar->data_lock); +} + +static void ath12k_core_trigger_partner(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_base *partner_ab; + bool found = false; + int i; + + for (i = 0; i < ag->num_devices; i++) { + partner_ab = ag->ab[i]; + if (!partner_ab) + continue; + + if (found) + ath12k_qmi_trigger_host_cap(partner_ab); + + found = (partner_ab == ab); + } +} + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) { - int ret; + struct ath12k_hw_group *ag = ath12k_ab_to_ag(ab); + int ret, i; - ret = ath12k_core_start_firmware(ab, ATH12K_FIRMWARE_MODE_NORMAL); + ret = ath12k_core_start_firmware(ab, ab->fw_mode); if (ret) { ath12k_err(ab, "failed to start firmware: %d\n", ret); return ret; @@ -831,41 +1277,54 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) goto err_firmware_stop; } + mutex_lock(&ag->mutex); mutex_lock(&ab->core_lock); - ret = ath12k_core_start(ab, ATH12K_FIRMWARE_MODE_NORMAL); + + ret = ath12k_core_start(ab); if (ret) { ath12k_err(ab, "failed to start core: %d\n", ret); goto err_dp_free; } - ret = ath12k_core_pdev_create(ab); - if (ret) { - ath12k_err(ab, "failed to create pdev core: %d\n", ret); - goto err_core_stop; - } - ath12k_hif_irq_enable(ab); + mutex_unlock(&ab->core_lock); - ret = ath12k_core_rfkill_config(ab); - if (ret && ret != -EOPNOTSUPP) { - ath12k_err(ab, "failed to config rfkill: %d\n", ret); - goto err_core_pdev_destroy; + if (ath12k_core_hw_group_start_ready(ag)) { + ret = ath12k_core_hw_group_start(ag); + if (ret) { + ath12k_warn(ab, "unable to start hw group\n"); + goto err_core_stop; + } + ath12k_dbg(ab, ATH12K_DBG_BOOT, "group %d started\n", ag->id); + } else { + ath12k_core_trigger_partner(ab); } - mutex_unlock(&ab->core_lock); + mutex_unlock(&ag->mutex); return 0; -err_core_pdev_destroy: - ath12k_core_pdev_destroy(ab); err_core_stop: - ath12k_core_stop(ab); - ath12k_mac_destroy(ab); + for (i = ag->num_devices - 1; i >= 0; i--) { + ab = ag->ab[i]; + if (!ab) + continue; + + mutex_lock(&ab->core_lock); + ath12k_core_stop(ab); + mutex_unlock(&ab->core_lock); + } + mutex_unlock(&ag->mutex); + goto exit; + err_dp_free: ath12k_dp_free(ab); mutex_unlock(&ab->core_lock); + mutex_unlock(&ag->mutex); + err_firmware_stop: ath12k_qmi_firmware_stop(ab); +exit: return ret; } @@ -874,9 +1333,8 @@ static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab) int ret; mutex_lock(&ab->core_lock); - ath12k_hif_irq_disable(ab); ath12k_dp_pdev_free(ab); - ath12k_hif_stop(ab); + ath12k_ce_cleanup_pipes(ab); ath12k_wmi_detach(ab); ath12k_dp_rx_pdev_reo_cleanup(ab); mutex_unlock(&ab->core_lock); @@ -908,6 +1366,7 @@ err_hal_srng_deinit: static void ath12k_rfkill_work(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, rfkill_work); + struct ath12k_hw_group *ag = ab->ag; struct ath12k *ar; struct ath12k_hw *ah; struct ieee80211_hw *hw; @@ -918,8 +1377,8 @@ static void ath12k_rfkill_work(struct work_struct *work) rfkill_radio_on = ab->rfkill_radio_on; spin_unlock_bh(&ab->base_lock); - for (i = 0; i < ab->num_hw; i++) { - ah = ab->ah[i]; + for (i = 0; i < ag->num_hw; i++) { + ah = ath12k_ag_to_ah(ag, i); if (!ah) continue; @@ -938,9 +1397,10 @@ static void ath12k_rfkill_work(struct work_struct *work) void ath12k_core_halt(struct ath12k *ar) { + struct list_head *pos, *n; struct ath12k_base *ab = ar->ab; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); ar->num_created_vdevs = 0; ar->allocated_vdev_map = 0; @@ -950,19 +1410,25 @@ void ath12k_core_halt(struct ath12k *ar) cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->rfkill_work); + cancel_work_sync(&ab->update_11d_work); rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); synchronize_rcu(); - INIT_LIST_HEAD(&ar->arvifs); + + spin_lock_bh(&ar->data_lock); + list_for_each_safe(pos, n, &ar->arvifs) + list_del_init(pos); + spin_unlock_bh(&ar->data_lock); + idr_init(&ar->txmgmt_idr); } static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) { + struct ath12k_hw_group *ag = ab->ag; struct ath12k *ar; - struct ath12k_pdev *pdev; struct ath12k_hw *ah; - int i; + int i, j; spin_lock_bh(&ab->base_lock); ab->stats.fw_crash_counter++; @@ -971,89 +1437,145 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) if (ab->is_reset) set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); - for (i = 0; i < ab->num_hw; i++) { - if (!ab->ah[i]) + for (i = 0; i < ag->num_hw; i++) { + ah = ath12k_ag_to_ah(ag, i); + if (!ah || ah->state == ATH12K_HW_STATE_OFF || + ah->state == ATH12K_HW_STATE_TM) continue; - ah = ab->ah[i]; - ieee80211_stop_queues(ah->hw); - } + wiphy_lock(ah->hw->wiphy); - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (!ar || ar->state == ATH12K_STATE_OFF) + /* If queue 0 is stopped, it is safe to assume that all + * other queues are stopped by driver via + * ieee80211_stop_queues() below. This means, there is + * no need to stop it again and hence continue + */ + if (ieee80211_queue_stopped(ah->hw, 0)) { + wiphy_unlock(ah->hw->wiphy); continue; + } + + ieee80211_stop_queues(ah->hw); - ath12k_mac_drain_tx(ar); - complete(&ar->scan.started); - complete(&ar->scan.completed); - complete(&ar->scan.on_channel); - complete(&ar->peer_assoc_done); - complete(&ar->peer_delete_done); - complete(&ar->install_key_done); - complete(&ar->vdev_setup_done); - complete(&ar->vdev_delete_done); - complete(&ar->bss_survey_done); + for (j = 0; j < ah->num_radio; j++) { + ar = &ah->radio[j]; - wake_up(&ar->dp.tx_empty_waitq); - idr_for_each(&ar->txmgmt_idr, - ath12k_mac_tx_mgmt_pending_free, ar); - idr_destroy(&ar->txmgmt_idr); - wake_up(&ar->txmgmt_empty_waitq); + ath12k_mac_drain_tx(ar); + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); + complete(&ar->scan.started); + complete_all(&ar->scan.completed); + complete(&ar->scan.on_channel); + complete(&ar->peer_assoc_done); + complete(&ar->peer_delete_done); + complete(&ar->install_key_done); + complete(&ar->vdev_setup_done); + complete(&ar->vdev_delete_done); + complete(&ar->bss_survey_done); + + wake_up(&ar->dp.tx_empty_waitq); + idr_for_each(&ar->txmgmt_idr, + ath12k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + wake_up(&ar->txmgmt_empty_waitq); + + ar->monitor_vdev_id = -1; + ar->monitor_vdev_created = false; + ar->monitor_started = false; + } + + wiphy_unlock(ah->hw->wiphy); } wake_up(&ab->wmi_ab.tx_credits_wq); wake_up(&ab->peer_mapping_wq); } -static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) +static void ath12k_update_11d(struct work_struct *work) { + struct ath12k_base *ab = container_of(work, struct ath12k_base, update_11d_work); struct ath12k *ar; struct ath12k_pdev *pdev; - int i; + struct wmi_set_current_country_arg arg = {}; + int ret, i; + + spin_lock_bh(&ab->base_lock); + memcpy(&arg.alpha2, &ab->new_alpha2, 2); + spin_unlock_bh(&ab->base_lock); + + ath12k_dbg(ab, ATH12K_DBG_WMI, "update 11d new cc %c%c\n", + arg.alpha2[0], arg.alpha2[1]); for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; - if (!ar || ar->state == ATH12K_STATE_OFF) + + memcpy(&ar->alpha2, &arg.alpha2, 2); + ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg); + if (ret) + ath12k_warn(ar->ab, + "pdev id %d failed set current country code: %d\n", + i, ret); + } +} + +static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_hw *ah; + struct ath12k *ar; + int i, j; + + for (i = 0; i < ag->num_hw; i++) { + ah = ath12k_ag_to_ah(ag, i); + if (!ah || ah->state == ATH12K_HW_STATE_OFF) continue; - mutex_lock(&ar->conf_mutex); + wiphy_lock(ah->hw->wiphy); + mutex_lock(&ah->hw_mutex); + + switch (ah->state) { + case ATH12K_HW_STATE_ON: + ah->state = ATH12K_HW_STATE_RESTARTING; + + for (j = 0; j < ah->num_radio; j++) { + ar = &ah->radio[j]; + ath12k_core_halt(ar); + } - switch (ar->state) { - case ATH12K_STATE_ON: - ar->state = ATH12K_STATE_RESTARTING; - ath12k_core_halt(ar); - ieee80211_restart_hw(ath12k_ar_to_hw(ar)); break; - case ATH12K_STATE_OFF: + case ATH12K_HW_STATE_OFF: ath12k_warn(ab, - "cannot restart radio %d that hasn't been started\n", + "cannot restart hw %d that hasn't been started\n", i); break; - case ATH12K_STATE_RESTARTING: + case ATH12K_HW_STATE_RESTARTING: break; - case ATH12K_STATE_RESTARTED: - ar->state = ATH12K_STATE_WEDGED; + case ATH12K_HW_STATE_RESTARTED: + ah->state = ATH12K_HW_STATE_WEDGED; fallthrough; - case ATH12K_STATE_WEDGED: + case ATH12K_HW_STATE_WEDGED: ath12k_warn(ab, - "device is wedged, will not restart radio %d\n", i); + "device is wedged, will not restart hw %d\n", i); + break; + case ATH12K_HW_STATE_TM: + ath12k_warn(ab, "fw mode reset done radio %d\n", i); break; } - mutex_unlock(&ar->conf_mutex); + + mutex_unlock(&ah->hw_mutex); + wiphy_unlock(ah->hw->wiphy); } + complete(&ab->driver_recovery); } static void ath12k_core_restart(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, restart_work); - int ret; - - if (!ab->is_reset) - ath12k_core_pre_reconfigure_recovery(ab); + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_hw *ah; + int ret, i; ret = ath12k_core_reconfigure_on_crash(ab); if (ret) { @@ -1061,20 +1583,42 @@ static void ath12k_core_restart(struct work_struct *work) return; } - if (ab->is_reset) - complete_all(&ab->reconfigure_complete); + if (ab->is_reset) { + if (!test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { + atomic_dec(&ab->reset_count); + complete(&ab->reset_complete); + ab->is_reset = false; + atomic_set(&ab->fail_cont_count, 0); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset success\n"); + } + + mutex_lock(&ag->mutex); + + if (!ath12k_core_hw_group_start_ready(ag)) { + mutex_unlock(&ag->mutex); + goto exit_restart; + } + + for (i = 0; i < ag->num_hw; i++) { + ah = ath12k_ag_to_ah(ag, i); + ieee80211_restart_hw(ah->hw); + } - if (!ab->is_reset) - ath12k_core_post_reconfigure_recovery(ab); + mutex_unlock(&ag->mutex); + } + +exit_restart: + complete(&ab->restart_completed); } static void ath12k_core_reset(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, reset_work); - int reset_count, fail_cont_count; + struct ath12k_hw_group *ag = ab->ag; + int reset_count, fail_cont_count, i; long time_left; - if (!(test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags))) { + if (!(test_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags))) { ath12k_warn(ab, "ignore reset dev flags 0x%lx\n", ab->dev_flags); return; } @@ -1117,25 +1661,47 @@ static void ath12k_core_reset(struct work_struct *work) ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset starting\n"); ab->is_reset = true; - atomic_set(&ab->recovery_start_count, 0); - reinit_completion(&ab->recovery_start); atomic_set(&ab->recovery_count, 0); + ath12k_coredump_collect(ab); ath12k_core_pre_reconfigure_recovery(ab); - reinit_completion(&ab->reconfigure_complete); ath12k_core_post_reconfigure_recovery(ab); ath12k_dbg(ab, ATH12K_DBG_BOOT, "waiting recovery start...\n"); - time_left = wait_for_completion_timeout(&ab->recovery_start, - ATH12K_RECOVER_START_TIMEOUT_HZ); + ath12k_hif_irq_disable(ab); + ath12k_hif_ce_irq_disable(ab); + + ath12k_hif_power_down(ab, false); + + /* prepare for power up */ + ab->qmi.num_radios = U8_MAX; + + mutex_lock(&ag->mutex); + ath12k_core_to_group_ref_put(ab); + + if (ag->num_started > 0) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "waiting for %d partner device(s) to reset\n", + ag->num_started); + mutex_unlock(&ag->mutex); + return; + } + + /* Prepare MLO global memory region for power up */ + ath12k_qmi_reset_mlo_mem(ag); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; - ath12k_hif_power_down(ab); - ath12k_qmi_free_resource(ab); - ath12k_hif_power_up(ab); + ath12k_hif_power_up(ab); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n"); + } - ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n"); + mutex_unlock(&ag->mutex); } int ath12k_core_pre_init(struct ath12k_base *ab) @@ -1153,32 +1719,456 @@ int ath12k_core_pre_init(struct ath12k_base *ab) return 0; } -int ath12k_core_init(struct ath12k_base *ab) +static int ath12k_core_panic_handler(struct notifier_block *nb, + unsigned long action, void *data) { - int ret; + struct ath12k_base *ab = container_of(nb, struct ath12k_base, + panic_nb); - ret = ath12k_core_soc_create(ab); - if (ret) { - ath12k_err(ab, "failed to create soc core: %d\n", ret); - return ret; + return ath12k_hif_panic_handler(ab); +} + +static int ath12k_core_panic_notifier_register(struct ath12k_base *ab) +{ + ab->panic_nb.notifier_call = ath12k_core_panic_handler; + + return atomic_notifier_chain_register(&panic_notifier_list, + &ab->panic_nb); +} + +static void ath12k_core_panic_notifier_unregister(struct ath12k_base *ab) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &ab->panic_nb); +} + +static inline +bool ath12k_core_hw_group_create_ready(struct ath12k_hw_group *ag) +{ + lockdep_assert_held(&ag->mutex); + + return (ag->num_probed == ag->num_devices); +} + +static struct ath12k_hw_group *ath12k_core_hw_group_alloc(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag; + int count = 0; + + lockdep_assert_held(&ath12k_hw_group_mutex); + + list_for_each_entry(ag, &ath12k_hw_group_list, list) + count++; + + ag = kzalloc(sizeof(*ag), GFP_KERNEL); + if (!ag) + return NULL; + + ag->id = count; + list_add(&ag->list, &ath12k_hw_group_list); + mutex_init(&ag->mutex); + ag->mlo_capable = false; + + return ag; +} + +static void ath12k_core_hw_group_free(struct ath12k_hw_group *ag) +{ + mutex_lock(&ath12k_hw_group_mutex); + + list_del(&ag->list); + kfree(ag); + + mutex_unlock(&ath12k_hw_group_mutex); +} + +static struct ath12k_hw_group *ath12k_core_hw_group_find_by_dt(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag; + int i; + + if (!ab->dev->of_node) + return NULL; + + list_for_each_entry(ag, &ath12k_hw_group_list, list) + for (i = 0; i < ag->num_devices; i++) + if (ag->wsi_node[i] == ab->dev->of_node) + return ag; + + return NULL; +} + +static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag, + struct ath12k_base *ab) +{ + struct device_node *wsi_dev = ab->dev->of_node, *next_wsi_dev; + struct device_node *tx_endpoint, *next_rx_endpoint; + int device_count = 0; + + next_wsi_dev = wsi_dev; + + if (!next_wsi_dev) + return -ENODEV; + + do { + ag->wsi_node[device_count] = next_wsi_dev; + + tx_endpoint = of_graph_get_endpoint_by_regs(next_wsi_dev, 0, -1); + if (!tx_endpoint) { + of_node_put(next_wsi_dev); + return -ENODEV; + } + + next_rx_endpoint = of_graph_get_remote_endpoint(tx_endpoint); + if (!next_rx_endpoint) { + of_node_put(next_wsi_dev); + of_node_put(tx_endpoint); + return -ENODEV; + } + + of_node_put(tx_endpoint); + of_node_put(next_wsi_dev); + + next_wsi_dev = of_graph_get_port_parent(next_rx_endpoint); + if (!next_wsi_dev) { + of_node_put(next_rx_endpoint); + return -ENODEV; + } + + of_node_put(next_rx_endpoint); + + device_count++; + if (device_count > ATH12K_MAX_DEVICES) { + ath12k_warn(ab, "device count in DT %d is more than limit %d\n", + device_count, ATH12K_MAX_DEVICES); + of_node_put(next_wsi_dev); + return -EINVAL; + } + } while (wsi_dev != next_wsi_dev); + + of_node_put(next_wsi_dev); + ag->num_devices = device_count; + + return 0; +} + +static int ath12k_core_get_wsi_index(struct ath12k_hw_group *ag, + struct ath12k_base *ab) +{ + int i, wsi_controller_index = -1, node_index = -1; + bool control; + + for (i = 0; i < ag->num_devices; i++) { + control = of_property_read_bool(ag->wsi_node[i], "qcom,wsi-controller"); + if (control) + wsi_controller_index = i; + + if (ag->wsi_node[i] == ab->dev->of_node) + node_index = i; + } + + if (wsi_controller_index == -1) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "wsi controller is not defined in dt"); + return -EINVAL; + } + + if (node_index == -1) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "unable to get WSI node index"); + return -EINVAL; } + ab->wsi_info.index = (ag->num_devices + node_index - wsi_controller_index) % + ag->num_devices; + return 0; } -void ath12k_core_deinit(struct ath12k_base *ab) +static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *ab) { - mutex_lock(&ab->core_lock); + struct ath12k_wsi_info *wsi = &ab->wsi_info; + struct ath12k_hw_group *ag; + + lockdep_assert_held(&ath12k_hw_group_mutex); + + if (ath12k_ftm_mode) + goto invalid_group; + + /* The grouping of multiple devices will be done based on device tree file. + * The platforms that do not have any valid group information would have + * each device to be part of its own invalid group. + * + * We use group id ATH12K_INVALID_GROUP_ID for single device group + * which didn't have dt entry or wrong dt entry, there could be many + * groups with same group id, i.e ATH12K_INVALID_GROUP_ID. So + * default group id of ATH12K_INVALID_GROUP_ID combined with + * num devices in ath12k_hw_group determines if the group is + * multi device or single device group + */ - ath12k_core_pdev_destroy(ab); - ath12k_core_stop(ab); + ag = ath12k_core_hw_group_find_by_dt(ab); + if (!ag) { + ag = ath12k_core_hw_group_alloc(ab); + if (!ag) { + ath12k_warn(ab, "unable to create new hw group\n"); + return NULL; + } - mutex_unlock(&ab->core_lock); + if (ath12k_core_get_wsi_info(ag, ab) || + ath12k_core_get_wsi_index(ag, ab)) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "unable to get wsi info from dt, grouping single device"); + ag->id = ATH12K_INVALID_GROUP_ID; + ag->num_devices = 1; + memset(ag->wsi_node, 0, sizeof(ag->wsi_node)); + wsi->index = 0; + } - ath12k_hif_power_down(ab); - ath12k_mac_destroy(ab); - ath12k_core_soc_destroy(ab); - ath12k_fw_unmap(ab); + goto exit; + } else if (test_bit(ATH12K_GROUP_FLAG_UNREGISTER, &ag->flags)) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "group id %d in unregister state\n", + ag->id); + goto invalid_group; + } else { + if (ath12k_core_get_wsi_index(ag, ab)) + goto invalid_group; + goto exit; + } + +invalid_group: + ag = ath12k_core_hw_group_alloc(ab); + if (!ag) { + ath12k_warn(ab, "unable to create new hw group\n"); + return NULL; + } + + ag->id = ATH12K_INVALID_GROUP_ID; + ag->num_devices = 1; + wsi->index = 0; + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "single device added to hardware group\n"); + +exit: + if (ag->num_probed >= ag->num_devices) { + ath12k_warn(ab, "unable to add new device to group, max limit reached\n"); + goto invalid_group; + } + + ab->device_id = ag->num_probed++; + ag->ab[ab->device_id] = ab; + ab->ag = ag; + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "wsi group-id %d num-devices %d index %d", + ag->id, ag->num_devices, wsi->index); + + return ag; +} + +void ath12k_core_hw_group_unassign(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ath12k_ab_to_ag(ab); + u8 device_id = ab->device_id; + int num_probed; + + if (!ag) + return; + + mutex_lock(&ag->mutex); + + if (WARN_ON(device_id >= ag->num_devices)) { + mutex_unlock(&ag->mutex); + return; + } + + if (WARN_ON(ag->ab[device_id] != ab)) { + mutex_unlock(&ag->mutex); + return; + } + + ag->ab[device_id] = NULL; + ab->ag = NULL; + ab->device_id = ATH12K_INVALID_DEVICE_ID; + + if (ag->num_probed) + ag->num_probed--; + + num_probed = ag->num_probed; + + mutex_unlock(&ag->mutex); + + if (!num_probed) + ath12k_core_hw_group_free(ag); +} + +static void ath12k_core_hw_group_destroy(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + if (WARN_ON(!ag)) + return; + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + ath12k_core_soc_destroy(ab); + } +} + +void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + if (!ag) + return; + + mutex_lock(&ag->mutex); + + if (test_bit(ATH12K_GROUP_FLAG_UNREGISTER, &ag->flags)) { + mutex_unlock(&ag->mutex); + return; + } + + set_bit(ATH12K_GROUP_FLAG_UNREGISTER, &ag->flags); + + ath12k_core_hw_group_stop(ag); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + mutex_lock(&ab->core_lock); + ath12k_core_stop(ab); + mutex_unlock(&ab->core_lock); + } + + mutex_unlock(&ag->mutex); +} + +static int ath12k_core_hw_group_create(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i, ret; + + lockdep_assert_held(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + mutex_lock(&ab->core_lock); + + ret = ath12k_core_soc_create(ab); + if (ret) { + mutex_unlock(&ab->core_lock); + ath12k_err(ab, "failed to create soc core: %d\n", ret); + return ret; + } + + mutex_unlock(&ab->core_lock); + } + + return 0; +} + +void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + if (ath12k_ftm_mode) + return; + + lockdep_assert_held(&ag->mutex); + + if (ag->num_devices == 1) { + ab = ag->ab[0]; + /* QCN9274 firmware uses firmware IE for MLO advertisement */ + if (ab->fw.fw_features_valid) { + ag->mlo_capable = + ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO); + return; + } + + /* while WCN7850 firmware uses QMI single_chip_mlo_support bit */ + ag->mlo_capable = ab->single_chip_mlo_support; + return; + } + + ag->mlo_capable = true; + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + /* even if 1 device's firmware feature indicates MLO + * unsupported, make MLO unsupported for the whole group + */ + if (!ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO)) { + ag->mlo_capable = false; + return; + } + } +} + +int ath12k_core_init(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag; + int ret; + + ret = ath12k_core_panic_notifier_register(ab); + if (ret) + ath12k_warn(ab, "failed to register panic handler: %d\n", ret); + + mutex_lock(&ath12k_hw_group_mutex); + + ag = ath12k_core_hw_group_assign(ab); + if (!ag) { + mutex_unlock(&ath12k_hw_group_mutex); + ath12k_warn(ab, "unable to get hw group\n"); + ret = -ENODEV; + goto err_unregister_notifier; + } + + mutex_unlock(&ath12k_hw_group_mutex); + + mutex_lock(&ag->mutex); + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "num devices %d num probed %d\n", + ag->num_devices, ag->num_probed); + + if (ath12k_core_hw_group_create_ready(ag)) { + ret = ath12k_core_hw_group_create(ag); + if (ret) { + mutex_unlock(&ag->mutex); + ath12k_warn(ab, "unable to create hw group\n"); + goto err_destroy_hw_group; + } + } + + mutex_unlock(&ag->mutex); + + return 0; + +err_destroy_hw_group: + ath12k_core_hw_group_destroy(ab->ag); + ath12k_core_hw_group_unassign(ab); +err_unregister_notifier: + ath12k_core_panic_notifier_unregister(ab); + + return ret; +} + +void ath12k_core_deinit(struct ath12k_base *ab) +{ + ath12k_core_hw_group_destroy(ab->ag); + ath12k_core_hw_group_unassign(ab); + ath12k_core_panic_notifier_unregister(ab); } void ath12k_core_free(struct ath12k_base *ab) @@ -1211,8 +2201,6 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, mutex_init(&ab->core_lock); spin_lock_init(&ab->base_lock); init_completion(&ab->reset_complete); - init_completion(&ab->reconfigure_complete); - init_completion(&ab->recovery_start); INIT_LIST_HEAD(&ab->peers); init_waitqueue_head(&ab->peer_mapping_wq); @@ -1220,14 +2208,28 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, INIT_WORK(&ab->restart_work, ath12k_core_restart); INIT_WORK(&ab->reset_work, ath12k_core_reset); INIT_WORK(&ab->rfkill_work, ath12k_rfkill_work); + INIT_WORK(&ab->dump_work, ath12k_coredump_upload); + INIT_WORK(&ab->update_11d_work, ath12k_update_11d); timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); + init_completion(&ab->restart_completed); + init_completion(&ab->wow.wakeup_completed); ab->dev = dev; ab->hif.bus = bus; ab->qmi.num_radios = U8_MAX; - ab->slo_capable = true; + ab->single_chip_mlo_support = false; + + /* Device index used to identify the devices in a group. + * + * In Intra-device MLO, only one device present in a group, + * so it is always zero. + * + * In Inter-device MLO, Multiple device present in a group, + * expect non-zero value. + */ + ab->device_id = 0; return ab; @@ -1238,5 +2240,31 @@ err_sc_free: return NULL; } -MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11be wireless LAN cards."); +static int ath12k_init(void) +{ + ahb_err = ath12k_ahb_init(); + if (ahb_err) + pr_warn("Failed to initialize ath12k AHB device: %d\n", ahb_err); + + pci_err = ath12k_pci_init(); + if (pci_err) + pr_warn("Failed to initialize ath12k PCI device: %d\n", pci_err); + + /* If both failed, return one of the failures (arbitrary) */ + return ahb_err && pci_err ? ahb_err : 0; +} + +static void ath12k_exit(void) +{ + if (!pci_err) + ath12k_pci_exit(); + + if (!ahb_err) + ath12k_ahb_exit(); +} + +module_init(ath12k_init); +module_exit(ath12k_exit); + +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11be WLAN devices"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 97e5a0ccd233..7bcd9c70309f 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_CORE_H @@ -14,6 +14,10 @@ #include <linux/dmi.h> #include <linux/ctype.h> #include <linux/firmware.h> +#include <linux/of_reserved_mem.h> +#include <linux/panic_notifier.h> +#include <linux/average.h> +#include <linux/of.h> #include "qmi.h" #include "htc.h" #include "wmi.h" @@ -26,6 +30,10 @@ #include "reg.h" #include "dbring.h" #include "fw.h" +#include "acpi.h" +#include "wow.h" +#include "debugfs_htt_stats.h" +#include "coredump.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -46,8 +54,7 @@ #define ATH12K_SMBIOS_BDF_EXT_MAGIC "BDF_" #define ATH12K_INVALID_HW_MAC_ID 0xFF -#define ATH12K_RX_RATE_TABLE_NUM 320 -#define ATH12K_RX_RATE_TABLE_11AX_NUM 576 +#define ATH12K_CONNECTION_LOSS_HZ (3 * HZ) #define ATH12K_MON_TIMER_INTERVAL 10 #define ATH12K_RESET_TIMEOUT_HZ (20 * HZ) @@ -57,6 +64,14 @@ #define ATH12K_RECONFIGURE_TIMEOUT_HZ (10 * HZ) #define ATH12K_RECOVER_START_TIMEOUT_HZ (20 * HZ) +#define ATH12K_MAX_DEVICES 3 +#define ATH12K_GROUP_MAX_RADIO (ATH12K_MAX_DEVICES * MAX_RADIOS) +#define ATH12K_INVALID_GROUP_ID 0xFF +#define ATH12K_INVALID_DEVICE_ID 0xFF + +#define ATH12K_MAX_MLO_PEERS 256 +#define ATH12K_MLO_PEER_ID_INVALID 0xFFFF + enum ath12k_bdf_search { ATH12K_BDF_SEARCH_DEFAULT, ATH12K_BDF_SEARCH_BUS_AND_BOARD, @@ -73,6 +88,7 @@ enum wme_ac { #define ATH12K_HT_MCS_MAX 7 #define ATH12K_VHT_MCS_MAX 9 #define ATH12K_HE_MCS_MAX 11 +#define ATH12K_EHT_MCS_MAX 15 enum ath12k_crypt_mode { /* Only use hardware crypto engine */ @@ -89,6 +105,14 @@ static inline enum wme_ac ath12k_tid_to_ac(u32 tid) WME_AC_VO); } +static inline u64 ath12k_le32hilo_to_u64(__le32 hi, __le32 lo) +{ + u64 hi64 = le32_to_cpu(hi); + u64 lo64 = le32_to_cpu(lo); + + return (hi64 << 32) | lo64; +} + enum ath12k_skb_flags { ATH12K_SKB_HW_80211_ENCAP = BIT(0), ATH12K_SKB_CIPHER_SET = BIT(1), @@ -101,6 +125,7 @@ struct ath12k_skb_cb { dma_addr_t paddr_ext_desc; u32 cipher; u8 flags; + u8 link_id; }; struct ath12k_skb_rxcb { @@ -113,17 +138,19 @@ struct ath12k_skb_rxcb { struct hal_rx_desc *rx_desc; u8 err_rel_src; u8 err_code; - u8 mac_id; + u8 hw_link_id; u8 unmapped; u8 is_frag; u8 tid; u16 peer_id; + bool is_end_of_ppdu; }; enum ath12k_hw_rev { ATH12K_HW_QCN9274_HW10, ATH12K_HW_QCN9274_HW20, - ATH12K_HW_WCN7850_HW20 + ATH12K_HW_WCN7850_HW20, + ATH12K_HW_IPQ5332_HW10, }; enum ath12k_firmware_mode { @@ -136,6 +163,7 @@ enum ath12k_firmware_mode { #define ATH12K_IRQ_NUM_MAX 57 #define ATH12K_EXT_IRQ_NUM_MAX 16 +#define ATH12K_MAX_TCL_RING_NUM 3 struct ath12k_ext_irq_grp { struct ath12k_base *ab; @@ -143,13 +171,39 @@ struct ath12k_ext_irq_grp { u32 num_irq; u32 grp_id; u64 timestamp; + bool napi_enabled; struct napi_struct napi; - struct net_device napi_ndev; + struct net_device *napi_ndev; +}; + +enum ath12k_smbios_cc_type { + /* disable country code setting from SMBIOS */ + ATH12K_SMBIOS_CC_DISABLE = 0, + + /* set country code by ANSI country name, based on ISO3166-1 alpha2 */ + ATH12K_SMBIOS_CC_ISO = 1, + + /* worldwide regdomain */ + ATH12K_SMBIOS_CC_WW = 2, }; struct ath12k_smbios_bdf { struct dmi_header hdr; - u32 padding; + u8 features_disabled; + + /* enum ath12k_smbios_cc_type */ + u8 country_code_flag; + + /* To set specific country, you need to set country code + * flag=ATH12K_SMBIOS_CC_ISO first, then if country is United + * States, then country code value = 0x5553 ("US",'U' = 0x55, 'S'= + * 0x53). To set country to INDONESIA, then country code value = + * 0x4944 ("IN", 'I'=0x49, 'D'=0x44). If country code flag = + * ATH12K_SMBIOS_CC_WW, then you can use worldwide regulatory + * setting. + */ + u16 cc_code; + u8 bdf_enabled; u8 bdf_ext[]; } __packed; @@ -178,8 +232,6 @@ struct ath12k_he { u32 heop_param; }; -#define MAX_RADIOS 3 - enum { WMI_HOST_TP_SCALE_MAX = 0, WMI_HOST_TP_SCALE_50 = 1, @@ -196,8 +248,19 @@ enum ath12k_scan_state { ATH12K_SCAN_ABORTING, }; +enum ath12k_11d_state { + ATH12K_11D_IDLE, + ATH12K_11D_PREPARING, + ATH12K_11D_RUNNING, +}; + +enum ath12k_hw_group_flags { + ATH12K_GROUP_FLAG_REGISTERED, + ATH12K_GROUP_FLAG_UNREGISTER, +}; + enum ath12k_dev_flags { - ATH12K_CAC_RUNNING, + ATH12K_FLAG_CAC_RUNNING, ATH12K_FLAG_CRASH_FLUSH, ATH12K_FLAG_RAW_MODE, ATH12K_FLAG_HW_CRYPTO_DISABLED, @@ -208,16 +271,39 @@ enum ath12k_dev_flags { ATH12K_FLAG_HTC_SUSPEND_COMPLETE, ATH12K_FLAG_CE_IRQ_ENABLED, ATH12K_FLAG_EXT_IRQ_ENABLED, + ATH12K_FLAG_QMI_FW_READY_COMPLETE, + ATH12K_FLAG_FTM_SEGMENTED, + ATH12K_FLAG_FIXED_MEM_REGION, }; -enum ath12k_monitor_flags { - ATH12K_FLAG_MONITOR_ENABLED, +struct ath12k_tx_conf { + bool changed; + u16 ac; + struct ieee80211_tx_queue_params tx_queue_params; }; -struct ath12k_vif { +struct ath12k_key_conf { + enum set_key_cmd cmd; + struct list_head list; + struct ieee80211_sta *sta; + struct ieee80211_key_conf *key; +}; + +struct ath12k_vif_cache { + struct ath12k_tx_conf tx_conf; + struct ath12k_key_conf key_conf; + u32 bss_conf_changed; +}; + +struct ath12k_rekey_data { + u8 kck[NL80211_KCK_LEN]; + u8 kek[NL80211_KCK_LEN]; + u64 replay_ctr; + bool enable_offload; +}; + +struct ath12k_link_vif { u32 vdev_id; - enum wmi_vdev_type vdev_type; - enum wmi_vdev_subtype vdev_subtype; u32 beacon_interval; u32 dtim_period; u16 ast_hash; @@ -227,13 +313,46 @@ struct ath12k_vif { u8 search_type; struct ath12k *ar; - struct ieee80211_vif *vif; int bank_id; u8 vdev_id_check_en; struct wmi_wmm_params_all_arg wmm_params; struct list_head list; + + bool is_created; + bool is_started; + bool is_up; + u8 bssid[ETH_ALEN]; + struct cfg80211_bitrate_mask bitrate_mask; + struct delayed_work connection_loss_work; + int num_legacy_stations; + int rtscts_prot_mode; + int txpower; + bool rsnie_present; + bool wpaie_present; + u8 vdev_stats_id; + u32 punct_bitmap; + u8 link_id; + struct ath12k_vif *ahvif; + struct ath12k_rekey_data rekey_data; + struct ath12k_link_stats link_stats; + spinlock_t link_stats_lock; /* Protects updates to link_stats */ + + u8 current_cntdown_counter; + + /* only used in station mode */ + bool is_sta_assoc_link; + + struct ath12k_reg_tpc_power_info reg_tpc_info; +}; + +struct ath12k_vif { + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + struct ieee80211_vif *vif; + struct ath12k_hw *ah; + union { struct { u32 uapsd; @@ -251,27 +370,31 @@ struct ath12k_vif { } ap; } u; - bool is_started; - bool is_up; u32 aid; - u8 bssid[ETH_ALEN]; - struct cfg80211_bitrate_mask bitrate_mask; - int num_legacy_stations; - int rtscts_prot_mode; - int txpower; - bool rsnie_present; - bool wpaie_present; - struct ieee80211_chanctx_conf chanctx; u32 key_cipher; u8 tx_encap_type; - u8 vdev_stats_id; - u32 punct_bitmap; bool ps; + atomic_t mcbc_gsn; + + struct ath12k_link_vif deflink; + struct ath12k_link_vif __rcu *link[ATH12K_NUM_MAX_LINKS]; + struct ath12k_vif_cache *cache[IEEE80211_MLD_MAX_NUM_LINKS]; + /* indicates bitmap of link vif created in FW */ + u16 links_map; + u8 last_scan_link; + + /* Must be last - ends in a flexible-array member. + * + * FIXME: Driver should not copy struct ieee80211_chanctx_conf, + * especially because it has a flexible array. Find a better way. + */ + struct ieee80211_chanctx_conf chanctx; }; struct ath12k_vif_iter { u32 vdev_id; - struct ath12k_vif *arvif; + struct ath12k *ar; + struct ath12k_link_vif *arvif; }; #define HAL_AST_IDX_INVALID 0xFFFF @@ -279,20 +402,22 @@ struct ath12k_vif_iter { #define HAL_RX_MAX_MCS_HT 31 #define HAL_RX_MAX_MCS_VHT 9 #define HAL_RX_MAX_MCS_HE 11 +#define HAL_RX_MAX_MCS_BE 15 #define HAL_RX_MAX_NSS 8 #define HAL_RX_MAX_NUM_LEGACY_RATES 12 -#define ATH12K_RX_RATE_TABLE_11AX_NUM 576 -#define ATH12K_RX_RATE_TABLE_NUM 320 + +#define ATH12K_SCAN_TIMEOUT_HZ (20 * HZ) struct ath12k_rx_peer_rate_stats { u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1]; u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1]; u64 he_mcs_count[HAL_RX_MAX_MCS_HE + 1]; + u64 be_mcs_count[HAL_RX_MAX_MCS_BE + 1]; u64 nss_count[HAL_RX_MAX_NSS]; u64 bw_count[HAL_RX_BW_MAX]; u64 gi_count[HAL_RX_GI_MAX]; u64 legacy_count[HAL_RX_MAX_NUM_LEGACY_RATES]; - u64 rx_rate[ATH12K_RX_RATE_TABLE_11AX_NUM]; + u64 rx_rate[HAL_RX_BW_MAX][HAL_RX_GI_MAX][HAL_RX_MAX_NSS][HAL_RX_MAX_MCS_HT + 1]; }; struct ath12k_rx_peer_stats { @@ -306,10 +431,6 @@ struct ath12k_rx_peer_stats { u64 non_ampdu_msdu_count; u64 stbc_count; u64 beamformed_count; - u64 mcs_count[HAL_RX_MAX_MCS + 1]; - u64 nss_count[HAL_RX_MAX_NSS]; - u64 bw_count[HAL_RX_BW_MAX]; - u64 gi_count[HAL_RX_GI_MAX]; u64 coding_count[HAL_RX_SU_MU_CODING_MAX]; u64 tid_count[IEEE80211_NUM_TIDS + 1]; u64 pream_cnt[HAL_RX_PREAMBLE_MAX]; @@ -406,51 +527,132 @@ struct ath12k_wbm_tx_stats { u64 wbm_tx_comp_stats[HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX]; }; -struct ath12k_sta { - struct ath12k_vif *arvif; +DECLARE_EWMA(avg_rssi, 10, 8) + +struct ath12k_link_sta { + struct ath12k_link_vif *arvif; + struct ath12k_sta *ahsta; + + /* link address similar to ieee80211_link_sta */ + u8 addr[ETH_ALEN]; /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ u32 bw; u32 nss; u32 smps; - enum hal_pn_type pn_type; - struct work_struct update_wk; + struct wiphy_work update_wk; struct rate_info txrate; struct rate_info last_txrate; u64 rx_duration; u64 tx_duration; u8 rssi_comb; + struct ewma_avg_rssi avg_rssi; + u8 link_id; struct ath12k_rx_peer_stats *rx_stats; struct ath12k_wbm_tx_stats *wbm_tx_stats; u32 bw_prev; + u32 peer_nss; + s8 rssi_beacon; + + /* For now the assoc link will be considered primary */ + bool is_assoc_link; + + /* for firmware use only */ + u8 link_idx; +}; + +struct ath12k_reoq_buf { + void *vaddr; + dma_addr_t paddr_aligned; + u32 size; +}; + +struct ath12k_sta { + struct ath12k_vif *ahvif; + enum hal_pn_type pn_type; + struct ath12k_link_sta deflink; + struct ath12k_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + /* indicates bitmap of link sta created in FW */ + u16 links_map; + u8 assoc_link_id; + u16 ml_peer_id; + u8 num_peer; + + enum ieee80211_sta_state state; + + struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; }; -#define ATH12K_MIN_5G_FREQ 4150 -#define ATH12K_MIN_6G_FREQ 5925 -#define ATH12K_MAX_6G_FREQ 7115 -#define ATH12K_NUM_CHANS 100 -#define ATH12K_MAX_5G_CHAN 173 +#define ATH12K_HALF_20MHZ_BW 10 +#define ATH12K_2GHZ_MIN_CENTER 2412 +#define ATH12K_2GHZ_MAX_CENTER 2484 +#define ATH12K_5GHZ_MIN_CENTER 4900 +#define ATH12K_5GHZ_MAX_CENTER 5920 +#define ATH12K_6GHZ_MIN_CENTER 5935 +#define ATH12K_6GHZ_MAX_CENTER 7115 +#define ATH12K_MIN_2GHZ_FREQ (ATH12K_2GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW - 1) +#define ATH12K_MAX_2GHZ_FREQ (ATH12K_2GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW + 1) +#define ATH12K_MIN_5GHZ_FREQ (ATH12K_5GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW) +#define ATH12K_MAX_5GHZ_FREQ (ATH12K_5GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW) +#define ATH12K_MIN_6GHZ_FREQ (ATH12K_6GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW) +#define ATH12K_MAX_6GHZ_FREQ (ATH12K_6GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW) +#define ATH12K_NUM_CHANS 101 +#define ATH12K_MAX_5GHZ_CHAN 173 + +static inline bool ath12k_is_2ghz_channel_freq(u32 freq) +{ + return freq >= ATH12K_MIN_2GHZ_FREQ && + freq <= ATH12K_MAX_2GHZ_FREQ; +} -enum ath12k_state { - ATH12K_STATE_OFF, - ATH12K_STATE_ON, - ATH12K_STATE_RESTARTING, - ATH12K_STATE_RESTARTED, - ATH12K_STATE_WEDGED, +enum ath12k_hw_state { + ATH12K_HW_STATE_OFF, + ATH12K_HW_STATE_ON, + ATH12K_HW_STATE_RESTARTING, + ATH12K_HW_STATE_RESTARTED, + ATH12K_HW_STATE_WEDGED, + ATH12K_HW_STATE_TM, /* Add other states as required */ }; /* Antenna noise floor */ #define ATH12K_DEFAULT_NOISE_FLOOR -95 +struct ath12k_ftm_event_obj { + u32 data_pos; + u32 expected_seq; + u8 *eventdata; +}; + struct ath12k_fw_stats { u32 pdev_id; u32 stats_id; struct list_head pdevs; struct list_head vdevs; struct list_head bcn; + u32 num_vdev_recvd; + u32 num_bcn_recvd; +}; + +struct ath12k_dbg_htt_stats { + enum ath12k_dbg_htt_ext_stats_type type; + u32 cfg_param[4]; + u8 reset; + struct debug_htt_stats_req *stats_req; +}; + +struct ath12k_debug { + struct dentry *debugfs_pdev; + struct dentry *debugfs_pdev_symlink; + struct ath12k_dbg_htt_stats htt_stats; + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type; + bool tpc_request; + struct completion tpc_complete; + struct wmi_tpc_stats_arg *tpc_stats; + u32 rx_filter; + bool extd_rx_stats; }; struct ath12k_per_peer_tx_stats { @@ -483,7 +685,6 @@ struct ath12k { u32 ht_cap_info; u32 vht_cap_info; struct ath12k_he ar_he; - enum ath12k_state state; bool supports_6ghz; struct { struct completion started; @@ -492,9 +693,10 @@ struct ath12k { struct delayed_work timeout; enum ath12k_scan_state state; bool is_roc; - int vdev_id; int roc_freq; bool roc_notify; + struct wiphy_work vdev_clean_wk; + struct ath12k_link_vif *arvif; } scan; struct { @@ -505,7 +707,6 @@ struct ath12k { unsigned long dev_flags; unsigned int filter_flags; - unsigned long monitor_flags; u32 min_tx_power; u32 max_tx_power; u32 txpower_limit_2g; @@ -515,13 +716,9 @@ struct ath12k { u32 chan_tx_pwr; u32 num_stations; u32 max_num_stations; - bool monitor_present; - /* To synchronize concurrent synchronous mac80211 callback operations, - * concurrent debugfs configuration and concurrent FW statistics events. - */ - struct mutex conf_mutex; + /* protects the radio specific data like debug stats, ppdu_stats_info stats, - * vdev_stop_status info, scan data, ath12k_sta info, ath12k_vif info, + * vdev_stop_status info, scan data, ath12k_sta info, ath12k_link_vif info, * channel context data, survey info, test mode data. */ spinlock_t data_lock; @@ -582,9 +779,12 @@ struct ath12k { struct work_struct regd_update_work; - struct work_struct wmi_mgmt_tx_work; + struct wiphy_work wmi_mgmt_tx_work; struct sk_buff_head wmi_mgmt_tx_queue; + struct ath12k_wow wow; + struct completion target_suspend; + bool target_suspend_ack; struct ath12k_per_peer_tx_stats peer_tx_stats; struct list_head ppdu_stats_info; u32 ppdu_stat_list_depth; @@ -592,18 +792,58 @@ struct ath12k { struct ath12k_per_peer_tx_stats cached_stats; u32 last_ppdu_id; u32 cached_ppdu_id; +#ifdef CONFIG_ATH12K_DEBUGFS + struct ath12k_debug debug; +#endif bool dfs_block_radar_events; - bool monitor_conf_enabled; bool monitor_vdev_created; bool monitor_started; int monitor_vdev_id; + + struct wiphy_radio_freq_range freq_range; + + bool nlo_enabled; + + /* Protected by wiphy::mtx lock. */ + u32 vdev_id_11d_scan; + struct completion completed_11d_scan; + enum ath12k_11d_state state_11d; + u8 alpha2[REG_ALPHA2_LEN]; + bool regdom_set_by_user; + + struct completion fw_stats_complete; + struct completion fw_stats_done; + + struct completion mlo_setup_done; + u32 mlo_setup_status; + u8 ftm_msgref; + struct ath12k_fw_stats fw_stats; + unsigned long last_tx_power_update; + + s8 max_allowed_tx_power; }; struct ath12k_hw { struct ieee80211_hw *hw; + struct device *dev; + + /* Protect the write operation of the hardware state ath12k_hw::state + * between hardware start<=>reconfigure<=>stop transitions. + */ + struct mutex hw_mutex; + enum ath12k_hw_state state; + bool regd_updated; + bool use_6ghz_regd; u8 num_radio; + + DECLARE_BITMAP(free_ml_peer_id_map, ATH12K_MAX_MLO_PEERS); + + /* protected by wiphy_lock() */ + struct list_head ml_peers; + + /* Keep last */ struct ath12k radio[] __aligned(sizeof(void *)); }; @@ -637,6 +877,8 @@ struct ath12k_pdev_cap { u32 tx_chain_mask_shift; u32 rx_chain_mask_shift; struct ath12k_band_cap band[NUM_NL80211_BANDS]; + u32 eml_cap; + u32 mld_cap; }; struct mlo_timestamp { @@ -653,6 +895,7 @@ struct mlo_timestamp { struct ath12k_pdev { struct ath12k *ar; u32 pdev_id; + u32 hw_link_id; struct ath12k_pdev_cap cap; u8 mac_addr[ETH_ALEN]; struct mlo_timestamp timestamp; @@ -670,7 +913,7 @@ struct ath12k_board_data { size_t len; }; -struct ath12k_soc_dp_tx_err_stats { +struct ath12k_device_dp_tx_err_stats { /* TCL Ring Descriptor unavailable */ u32 desc_na[DP_TCL_NUM_RING_MAX]; /* Other failures during dp_tx due to mem allocation failure @@ -679,13 +922,72 @@ struct ath12k_soc_dp_tx_err_stats { atomic_t misc_fail; }; -struct ath12k_soc_dp_stats { +struct ath12k_device_dp_stats { u32 err_ring_pkts; u32 invalid_rbm; u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; u32 hal_reo_error[DP_REO_DST_RING_MAX]; - struct ath12k_soc_dp_tx_err_stats tx_err; + struct ath12k_device_dp_tx_err_stats tx_err; + u32 reo_rx[DP_REO_DST_RING_MAX][ATH12K_MAX_DEVICES]; + u32 rx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX][ATH12K_MAX_DEVICES]; + u32 tqm_rel_reason[MAX_TQM_RELEASE_REASON]; + u32 fw_tx_status[MAX_FW_TX_STATUS]; + u32 tx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX]; + u32 tx_enqueued[DP_TCL_NUM_RING_MAX]; + u32 tx_completed[DP_TCL_NUM_RING_MAX]; +}; + +struct ath12k_reg_freq { + u32 start_freq; + u32 end_freq; +}; + +struct ath12k_mlo_memory { + struct target_mem_chunk chunk[ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; + int mlo_mem_size; + bool init_done; +}; + +struct ath12k_hw_link { + u8 device_id; + u8 pdev_idx; +}; + +/* Holds info on the group of devices that are registered as a single + * wiphy, protected with struct ath12k_hw_group::mutex. + */ +struct ath12k_hw_group { + struct list_head list; + u8 id; + u8 num_devices; + u8 num_probed; + u8 num_started; + unsigned long flags; + struct ath12k_base *ab[ATH12K_MAX_DEVICES]; + + /* protects access to this struct */ + struct mutex mutex; + + /* Holds information of wiphy (hw) registration. + * + * In Multi/Single Link Operation case, all pdevs are registered as + * a single wiphy. In other (legacy/Non-MLO) cases, each pdev is + * registered as separate wiphys. + */ + struct ath12k_hw *ah[ATH12K_GROUP_MAX_RADIO]; + u8 num_hw; + bool mlo_capable; + struct device_node *wsi_node[ATH12K_MAX_DEVICES]; + struct ath12k_mlo_memory mlo_mem; + struct ath12k_hw_link hw_links[ATH12K_GROUP_MAX_RADIO]; + bool hw_link_id_init_done; +}; + +/* Holds WSI info specific to each device, excluding WSI group info */ +struct ath12k_wsi_info { + u32 index; + u32 hw_link_id_base; }; /* Master structure to hold the hw data which may be used in core module */ @@ -696,10 +998,15 @@ struct ath12k_base { struct ath12k_qmi qmi; struct ath12k_wmi_base wmi_ab; struct completion fw_ready; + u8 device_id; int num_radios; /* HW channel counters frequency value in hertz common to all MACs */ u32 cc_freq_hz; + struct ath12k_dump_file_data *dump_data; + size_t ath12k_coredump_len; + struct work_struct dump_work; + struct ath12k_htc htc; struct ath12k_dp dp; @@ -707,11 +1014,20 @@ struct ath12k_base { void __iomem *mem; unsigned long mem_len; + void __iomem *mem_ce; + u32 ce_remap_base_addr; + bool ce_remap; + struct { enum ath12k_bus bus; const struct ath12k_hif_ops *ops; } hif; + struct { + struct completion wakeup_completed; + u32 wmi_conf_rx_decap_mode; + } wow; + struct ath12k_ce ce; struct timer_list rx_replenish_retry; struct ath12k_hal hal; @@ -741,15 +1057,6 @@ struct ath12k_base { struct ath12k_pdev __rcu *pdevs_active[MAX_RADIOS]; - /* Holds information of wiphy (hw) registration. - * - * In Multi/Single Link Operation case, all pdevs are registered as - * a single wiphy. In other (legacy/Non-MLO) cases, each pdev is - * registered as separate wiphys. - */ - struct ath12k_hw *ah[MAX_RADIOS]; - u8 num_hw; - struct ath12k_wmi_hal_reg_capabilities_ext_arg hal_reg_cap[MAX_RADIOS]; unsigned long long free_vdev_map; unsigned long long free_vdev_stats_id_map; @@ -779,9 +1086,14 @@ struct ath12k_base { */ struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + struct ath12k_reg_info *reg_info[MAX_RADIOS]; + /* Current DFS Regulatory */ enum ath12k_dfs_region dfs_region; - struct ath12k_soc_dp_stats soc_stats; + struct ath12k_device_dp_stats device_stats; +#ifdef CONFIG_ATH12K_DEBUGFS + struct dentry *debugfs_soc; +#endif unsigned long dev_flags; struct completion driver_recovery; @@ -791,14 +1103,13 @@ struct ath12k_base { struct work_struct reset_work; atomic_t reset_count; atomic_t recovery_count; - atomic_t recovery_start_count; bool is_reset; struct completion reset_complete; - struct completion reconfigure_complete; - struct completion recovery_start; /* continuous recovery fail count */ atomic_t fail_cont_count; unsigned long reset_fail_timeout; + struct work_struct update_11d_work; + u8 new_alpha2[2]; struct { /* protected by data_lock */ u32 fw_crash_counter; @@ -808,8 +1119,6 @@ struct ath12k_base { struct ath12k_dbring_cap *db_caps; u32 num_db_cap; - struct timer_list mon_reap_timer; - struct completion htc_suspend; u64 fw_soc_drop_count; @@ -839,14 +1148,51 @@ struct ath12k_base { size_t m3_len; DECLARE_BITMAP(fw_features, ATH12K_FW_FEATURE_COUNT); + bool fw_features_valid; } fw; const struct hal_rx_ops *hal_rx_ops; - /* slo_capable denotes if the single/multi link operation - * is supported within the same chip (SoC). - */ - bool slo_capable; + struct completion restart_completed; + +#ifdef CONFIG_ACPI + + struct { + bool started; + u32 func_bit; + bool acpi_tas_enable; + bool acpi_bios_sar_enable; + bool acpi_disable_11be; + bool acpi_disable_rfkill; + bool acpi_cca_enable; + bool acpi_band_edge_enable; + bool acpi_enable_bdf; + u32 bit_flag; + char bdf_string[ATH12K_ACPI_BDF_MAX_LEN]; + u8 tas_cfg[ATH12K_ACPI_DSM_TAS_CFG_SIZE]; + u8 tas_sar_power_table[ATH12K_ACPI_DSM_TAS_DATA_SIZE]; + u8 bios_sar_data[ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE]; + u8 geo_offset_data[ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE]; + u8 cca_data[ATH12K_ACPI_DSM_CCA_DATA_SIZE]; + u8 band_edge_power[ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE]; + } acpi; + +#endif /* CONFIG_ACPI */ + + struct notifier_block panic_nb; + + struct ath12k_hw_group *ag; + struct ath12k_wsi_info wsi_info; + enum ath12k_firmware_mode fw_mode; + struct ath12k_ftm_event_obj ftm_event_obj; + bool hw_group_ref; + + /* Denote whether MLO is possible within the device */ + bool single_chip_mlo_support; + + struct ath12k_reg_freq reg_freq_2ghz; + struct ath12k_reg_freq reg_freq_5ghz; + struct ath12k_reg_freq reg_freq_6ghz; /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); @@ -857,7 +1203,95 @@ struct ath12k_pdev_map { u8 pdev_idx; }; +struct ath12k_fw_stats_vdev { + struct list_head list; + + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +}; + +struct ath12k_fw_stats_bcn { + struct list_head list; + + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +}; + +struct ath12k_fw_stats_pdev { + struct list_head list; + + /* PDEV stats */ + s32 ch_noise_floor; + u32 tx_frame_count; + u32 rx_frame_count; + u32 rx_clear_count; + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; + + /* PDEV TX stats */ + s32 comp_queued; + s32 comp_delivered; + s32 msdu_enqued; + s32 mpdu_enqued; + s32 wmm_drop; + s32 local_enqued; + s32 local_freed; + s32 hw_queued; + s32 hw_reaped; + s32 underrun; + s32 tx_abort; + s32 mpdus_requed; + u32 tx_ko; + u32 data_rc; + u32 self_triggers; + u32 sw_retry_failure; + u32 illgl_rate_phy_err; + u32 pdev_cont_xretry; + u32 pdev_tx_timeout; + u32 pdev_resets; + u32 stateless_tid_alloc_failure; + u32 phy_underrun; + u32 txop_ovf; + + /* PDEV RX stats */ + s32 mid_ppdu_route_change; + s32 status_rcvd; + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + s32 htt_msdus; + s32 htt_mpdus; + s32 loc_msdus; + s32 loc_mpdus; + s32 oversize_amsdu; + s32 phy_errs; + s32 phy_err_drop; + s32 mpdu_errs; +}; + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab); +void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag); int ath12k_core_pre_init(struct ath12k_base *ab); int ath12k_core_init(struct ath12k_base *ath12k); void ath12k_core_deinit(struct ath12k_base *ath12k); @@ -874,8 +1308,12 @@ int ath12k_core_fetch_regdb(struct ath12k_base *ab, struct ath12k_board_data *bd int ath12k_core_check_dt(struct ath12k_base *ath12k); int ath12k_core_check_smbios(struct ath12k_base *ab); void ath12k_core_halt(struct ath12k *ar); +int ath12k_core_resume_early(struct ath12k_base *ab); int ath12k_core_resume(struct ath12k_base *ab); int ath12k_core_suspend(struct ath12k_base *ab); +int ath12k_core_suspend_late(struct ath12k_base *ab); +void ath12k_core_hw_group_unassign(struct ath12k_base *ab); +u8 ath12k_get_num_partner_link(struct ath12k *ar); const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, const char *filename); @@ -883,6 +1321,14 @@ u32 ath12k_core_get_max_station_per_radio(struct ath12k_base *ab); u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab); u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab); +void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag); +void ath12k_fw_stats_init(struct ath12k *ar); +void ath12k_fw_stats_bcn_free(struct list_head *head); +void ath12k_fw_stats_free(struct ath12k_fw_stats *stats); +void ath12k_fw_stats_reset(struct ath12k *ar); +struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab, + int index); + static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state) { switch (state) { @@ -912,16 +1358,26 @@ static inline struct ath12k_skb_rxcb *ATH12K_SKB_RXCB(struct sk_buff *skb) return (struct ath12k_skb_rxcb *)skb->cb; } -static inline struct ath12k_vif *ath12k_vif_to_arvif(struct ieee80211_vif *vif) +static inline struct ath12k_vif *ath12k_vif_to_ahvif(struct ieee80211_vif *vif) { return (struct ath12k_vif *)vif->drv_priv; } -static inline struct ath12k_sta *ath12k_sta_to_arsta(struct ieee80211_sta *sta) +static inline struct ath12k_sta *ath12k_sta_to_ahsta(struct ieee80211_sta *sta) { return (struct ath12k_sta *)sta->drv_priv; } +static inline struct ieee80211_sta *ath12k_ahsta_to_sta(struct ath12k_sta *ahsta) +{ + return container_of((void *)ahsta, struct ieee80211_sta, drv_priv); +} + +static inline struct ieee80211_vif *ath12k_ahvif_to_vif(struct ath12k_vif *ahvif) +{ + return container_of((void *)ahvif, struct ieee80211_vif, drv_priv); +} + static inline struct ath12k *ath12k_ab_to_ar(struct ath12k_base *ab, int mac_id) { @@ -932,8 +1388,16 @@ static inline void ath12k_core_create_firmware_path(struct ath12k_base *ab, const char *filename, void *buf, size_t buf_len) { - snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR, - ab->hw_params->fw.dir, filename); + const char *fw_name = NULL; + + of_property_read_string(ab->dev->of_node, "firmware-name", &fw_name); + + if (fw_name && strncmp(filename, "board", 5)) + snprintf(buf, buf_len, "%s/%s/%s/%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, fw_name, filename); + else + snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, filename); } static inline const char *ath12k_bus_str(enum ath12k_bus bus) @@ -941,6 +1405,8 @@ static inline const char *ath12k_bus_str(enum ath12k_bus bus) switch (bus) { case ATH12K_BUS_PCI: return "pci"; + case ATH12K_BUS_AHB: + return "ahb"; } return "unknown"; @@ -951,13 +1417,49 @@ static inline struct ath12k_hw *ath12k_hw_to_ah(struct ieee80211_hw *hw) return hw->priv; } -static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah) +static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah, u8 hw_link_id) +{ + if (WARN(hw_link_id >= ah->num_radio, + "bad hw link id %d, so switch to default link\n", hw_link_id)) + hw_link_id = 0; + + return &ah->radio[hw_link_id]; +} + +static inline struct ath12k_hw *ath12k_ar_to_ah(struct ath12k *ar) { - return ah->radio; + return ar->ah; } static inline struct ieee80211_hw *ath12k_ar_to_hw(struct ath12k *ar) { return ar->ah->hw; } + +#define for_each_ar(ah, ar, index) \ + for ((index) = 0; ((index) < (ah)->num_radio && \ + ((ar) = &(ah)->radio[(index)])); (index)++) + +static inline struct ath12k_hw *ath12k_ag_to_ah(struct ath12k_hw_group *ag, int idx) +{ + return ag->ah[idx]; +} + +static inline void ath12k_ag_set_ah(struct ath12k_hw_group *ag, int idx, + struct ath12k_hw *ah) +{ + ag->ah[idx] = ah; +} + +static inline struct ath12k_hw_group *ath12k_ab_to_ag(struct ath12k_base *ab) +{ + return ab->ag; +} + +static inline struct ath12k_base *ath12k_ag_to_ab(struct ath12k_hw_group *ag, + u8 device_id) +{ + return ag->ab[device_id]; +} + #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/coredump.c b/drivers/net/wireless/ath/ath12k/coredump.c new file mode 100644 index 000000000000..ce1beeb54836 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/coredump.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include <linux/devcoredump.h> +#include "hif.h" +#include "coredump.h" +#include "debug.h" + +enum +ath12k_fw_crash_dump_type ath12k_coredump_get_dump_type(enum ath12k_qmi_target_mem type) +{ + enum ath12k_fw_crash_dump_type dump_type; + + switch (type) { + case HOST_DDR_REGION_TYPE: + dump_type = FW_CRASH_DUMP_REMOTE_MEM_DATA; + break; + case M3_DUMP_REGION_TYPE: + dump_type = FW_CRASH_DUMP_M3_DUMP; + break; + case PAGEABLE_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_PAGEABLE_DATA; + break; + case BDF_MEM_REGION_TYPE: + case CALDB_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_NONE; + break; + case MLO_GLOBAL_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_MLO_GLOBAL_DATA; + break; + default: + dump_type = FW_CRASH_DUMP_TYPE_MAX; + break; + } + + return dump_type; +} + +void ath12k_coredump_upload(struct work_struct *work) +{ + struct ath12k_base *ab = container_of(work, struct ath12k_base, dump_work); + + ath12k_info(ab, "Uploading coredump\n"); + /* dev_coredumpv() takes ownership of the buffer */ + dev_coredumpv(ab->dev, ab->dump_data, ab->ath12k_coredump_len, GFP_KERNEL); + ab->dump_data = NULL; +} + +void ath12k_coredump_collect(struct ath12k_base *ab) +{ + ath12k_hif_coredump_download(ab); +} diff --git a/drivers/net/wireless/ath/ath12k/coredump.h b/drivers/net/wireless/ath/ath12k/coredump.h new file mode 100644 index 000000000000..13f46a605113 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/coredump.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef _ATH12K_COREDUMP_H_ +#define _ATH12K_COREDUMP_H_ + +#define ATH12K_FW_CRASH_DUMP_V2 2 + +enum ath12k_fw_crash_dump_type { + FW_CRASH_DUMP_PAGING_DATA, + FW_CRASH_DUMP_RDDM_DATA, + FW_CRASH_DUMP_REMOTE_MEM_DATA, + FW_CRASH_DUMP_PAGEABLE_DATA, + FW_CRASH_DUMP_M3_DUMP, + FW_CRASH_DUMP_NONE, + FW_CRASH_DUMP_MLO_GLOBAL_DATA, + + /* keep last */ + FW_CRASH_DUMP_TYPE_MAX, +}; + +#define COREDUMP_TLV_HDR_SIZE 8 + +struct ath12k_tlv_dump_data { + /* see ath11k_fw_crash_dump_type above */ + __le32 type; + + /* in bytes */ + __le32 tlv_len; + + /* pad to 32-bit boundaries as needed */ + u8 tlv_data[]; +} __packed; + +struct ath12k_dump_file_data { + /* "ATH12K-FW-DUMP" */ + char df_magic[16]; + /* total dump len in bytes */ + __le32 len; + /* file dump version */ + __le32 version; + /* pci device id */ + __le32 chip_id; + /* qrtr instance id */ + __le32 qrtr_id; + /* pci domain id */ + __le32 bus_id; + guid_t guid; + /* time-of-day stamp */ + __le64 tv_sec; + /* time-of-day stamp, nano-seconds */ + __le64 tv_nsec; + /* room for growth w/out changing binary format */ + u8 unused[128]; + u8 data[]; +} __packed; + +#ifdef CONFIG_ATH12K_COREDUMP +enum ath12k_fw_crash_dump_type ath12k_coredump_get_dump_type + (enum ath12k_qmi_target_mem type); +void ath12k_coredump_upload(struct work_struct *work); +void ath12k_coredump_collect(struct ath12k_base *ab); +#else +static inline enum ath12k_fw_crash_dump_type ath12k_coredump_get_dump_type + (enum ath12k_qmi_target_mem type) +{ + return FW_CRASH_DUMP_TYPE_MAX; +} + +static inline void ath12k_coredump_upload(struct work_struct *work) +{ +} + +static inline void ath12k_coredump_collect(struct ath12k_base *ab) +{ +} +#endif + +#endif diff --git a/drivers/net/wireless/ath/ath12k/debug.c b/drivers/net/wireless/ath/ath12k/debug.c index fe5a732ba9ec..5ce100cd9a9d 100644 --- a/drivers/net/wireless/ath/ath12k/debug.c +++ b/drivers/net/wireless/ath/ath12k/debug.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/vmalloc.h> @@ -36,7 +36,7 @@ void ath12k_err(struct ath12k_base *ab, const char *fmt, ...) va_end(args); } -void ath12k_warn(struct ath12k_base *ab, const char *fmt, ...) +void __ath12k_warn(struct device *dev, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, @@ -45,7 +45,7 @@ void ath12k_warn(struct ath12k_base *ab, const char *fmt, ...) va_start(args, fmt); vaf.va = &args; - dev_warn_ratelimited(ab->dev, "%pV", &vaf); + dev_warn_ratelimited(dev, "%pV", &vaf); /* TODO: Trace the log */ va_end(args); } @@ -63,8 +63,10 @@ void __ath12k_dbg(struct ath12k_base *ab, enum ath12k_debug_mask mask, vaf.fmt = fmt; vaf.va = &args; - if (ath12k_debug_mask & mask) + if (likely(ab)) dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + else + printk(KERN_DEBUG "ath12k: %pV", &vaf); /* TODO: trace log */ diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index aa685295f8a4..48916e4e1f60 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_DEBUG_H_ @@ -25,14 +25,19 @@ enum ath12k_debug_mask { ATH12K_DBG_PCI = 0x00001000, ATH12K_DBG_DP_TX = 0x00002000, ATH12K_DBG_DP_RX = 0x00004000, + ATH12K_DBG_WOW = 0x00008000, ATH12K_DBG_ANY = 0xffffffff, }; __printf(2, 3) void ath12k_info(struct ath12k_base *ab, const char *fmt, ...); __printf(2, 3) void ath12k_err(struct ath12k_base *ab, const char *fmt, ...); -__printf(2, 3) void ath12k_warn(struct ath12k_base *ab, const char *fmt, ...); +__printf(2, 3) void __ath12k_warn(struct device *dev, const char *fmt, ...); + +#define ath12k_warn(ab, fmt, ...) __ath12k_warn((ab)->dev, fmt, ##__VA_ARGS__) +#define ath12k_hw_warn(ah, fmt, ...) __ath12k_warn((ah)->dev, fmt, ##__VA_ARGS__) extern unsigned int ath12k_debug_mask; +extern bool ath12k_ftm_mode; #ifdef CONFIG_ATH12K_DEBUG __printf(3, 4) void __ath12k_dbg(struct ath12k_base *ab, @@ -57,11 +62,14 @@ static inline void ath12k_dbg_dump(struct ath12k_base *ab, } #endif /* CONFIG_ATH12K_DEBUG */ -#define ath12k_dbg(ar, dbg_mask, fmt, ...) \ +#define ath12k_dbg(ab, dbg_mask, fmt, ...) \ do { \ typeof(dbg_mask) mask = (dbg_mask); \ if (ath12k_debug_mask & mask) \ - __ath12k_dbg(ar, mask, fmt, ##__VA_ARGS__); \ + __ath12k_dbg(ab, mask, fmt, ##__VA_ARGS__); \ } while (0) +#define ath12k_generic_dbg(dbg_mask, fmt, ...) \ + ath12k_dbg(NULL, dbg_mask, fmt, ##__VA_ARGS__) + #endif /* _ATH12K_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c new file mode 100644 index 000000000000..23da93afaa5c --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -0,0 +1,1515 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "core.h" +#include "dp_tx.h" +#include "debug.h" +#include "debugfs.h" +#include "debugfs_htt_stats.h" + +static ssize_t ath12k_write_simulate_radar(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + int ret; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + ret = ath12k_wmi_simulate_radar(ar); + if (ret) + goto exit; + + ret = count; +exit: + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + return ret; +} + +static const struct file_operations fops_simulate_radar = { + .write = ath12k_write_simulate_radar, + .open = simple_open +}; + +static ssize_t ath12k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = + "To simulate firmware crash write one of the keywords to this file:\n" + "`assert` - send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"; + + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static ssize_t +ath12k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k_base *ab = file->private_data; + struct ath12k_pdev *pdev; + struct ath12k *ar = NULL; + char buf[32] = {0}; + int i, ret; + ssize_t rc; + + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ar) + break; + } + + if (!ar) + return -ENETDOWN; + + if (!strcmp(buf, "assert")) { + ath12k_info(ab, "simulating firmware assert crash\n"); + ret = ath12k_wmi_force_fw_hang_cmd(ar, + ATH12K_WMI_FW_HANG_ASSERT_TYPE, + ATH12K_WMI_FW_HANG_DELAY); + } else { + return -EINVAL; + } + + if (ret) { + ath12k_warn(ab, "failed to simulate firmware crash: %d\n", ret); + return ret; + } + + return count; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath12k_read_simulate_fw_crash, + .write = ath12k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_write_tpc_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + u8 type; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= WMI_HALPHY_PDEV_TX_STATS_MAX) + return -EINVAL; + + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_stats_type = type; + spin_unlock_bh(&ar->data_lock); + + return count; +} + +static int ath12k_debug_tpc_stats_request(struct ath12k *ar) +{ + enum wmi_halphy_ctrl_path_stats_id tpc_stats_sub_id; + struct ath12k_base *ab = ar->ab; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + reinit_completion(&ar->debug.tpc_complete); + + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_request = true; + tpc_stats_sub_id = ar->debug.tpc_stats_type; + spin_unlock_bh(&ar->data_lock); + + ret = ath12k_wmi_send_tpc_stats_request(ar, tpc_stats_sub_id); + if (ret) { + ath12k_warn(ab, "failed to request pdev tpc stats: %d\n", ret); + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_request = false; + spin_unlock_bh(&ar->data_lock); + return ret; + } + + return 0; +} + +static int ath12k_get_tpc_ctl_mode_idx(struct wmi_tpc_stats_arg *tpc_stats, + enum wmi_tpc_pream_bw pream_bw, int *mode_idx) +{ + u32 chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq); + u8 band; + + band = ((chan_freq > ATH12K_MIN_6GHZ_FREQ) ? NL80211_BAND_6GHZ : + ((chan_freq > ATH12K_MIN_5GHZ_FREQ) ? NL80211_BAND_5GHZ : + NL80211_BAND_2GHZ)); + + if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) { + switch (pream_bw) { + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_VHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_EHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HT40: + case WMI_TPC_PREAM_VHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_EHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_VHT80: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT60: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20; + break; + case WMI_TPC_PREAM_HE80: + case WMI_TPC_PREAM_EHT80: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_VHT160: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT120: + case WMI_TPC_PREAM_EHT140: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20; + break; + case WMI_TPC_PREAM_HE160: + case WMI_TPC_PREAM_EHT160: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT200: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120; + break; + case WMI_TPC_PREAM_EHT240: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80; + break; + case WMI_TPC_PREAM_EHT280: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40; + break; + case WMI_TPC_PREAM_EHT320: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ; + break; + default: + /* for 5GHZ and 6GHZ, default case will be for OFDM */ + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ; + break; + } + } else { + switch (pream_bw) { + case WMI_TPC_PREAM_OFDM: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ; + break; + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_VHT20: + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_EHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ; + break; + case WMI_TPC_PREAM_HT40: + case WMI_TPC_PREAM_VHT40: + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_EHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ; + break; + default: + /* for 2GHZ, default case will be CCK */ + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ; + break; + } + } + + return 0; +} + +static s16 ath12k_tpc_get_rate(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + u32 rate_idx, u32 num_chains, u32 rate_code, + enum wmi_tpc_pream_bw pream_bw, + enum wmi_halphy_ctrl_path_stats_id type, + u32 eht_rate_idx) +{ + u32 tot_nss, tot_modes, txbf_on_off, index_offset1, index_offset2, index_offset3; + u8 chain_idx, stm_idx, num_streams; + bool is_mu, txbf_enabled = 0; + s8 rates_ctl_min, tpc_ctl; + s16 rates, tpc, reg_pwr; + u16 rate1, rate2; + int mode, ret; + + num_streams = 1 + ATH12K_HW_NSS(rate_code); + chain_idx = num_chains - 1; + stm_idx = num_streams - 1; + mode = -1; + + ret = ath12k_get_tpc_ctl_mode_idx(tpc_stats, pream_bw, &mode); + if (ret) { + ath12k_warn(ar->ab, "Invalid mode index received\n"); + tpc = TPC_INVAL; + goto out; + } + + if (num_chains < num_streams) { + tpc = TPC_INVAL; + goto out; + } + + if (le32_to_cpu(tpc_stats->tpc_config.num_tx_chain) <= 1) { + tpc = TPC_INVAL; + goto out; + } + + if (type == WMI_HALPHY_PDEV_TX_SUTXBF_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) + txbf_enabled = 1; + + if (type == WMI_HALPHY_PDEV_TX_MU_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) { + is_mu = true; + } else { + is_mu = false; + } + + /* Below is the min calculation of ctl array, rates array and + * regulator power table. tpc is minimum of all 3 + */ + if (pream_bw >= WMI_TPC_PREAM_EHT20 && pream_bw <= WMI_TPC_PREAM_EHT320) { + rate2 = tpc_stats->rates_array2.rate_array[eht_rate_idx]; + if (is_mu) + rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_MU); + else + rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_SU); + } else { + rate1 = tpc_stats->rates_array1.rate_array[rate_idx]; + if (is_mu) + rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_MU); + else + rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_SU); + } + + if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) { + tot_nss = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d1); + tot_modes = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d2); + txbf_on_off = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d3); + index_offset1 = txbf_on_off * tot_modes * tot_nss; + index_offset2 = tot_modes * tot_nss; + index_offset3 = tot_nss; + + tpc_ctl = *(tpc_stats->ctl_array.ctl_pwr_table + + chain_idx * index_offset1 + txbf_enabled * index_offset2 + + mode * index_offset3 + stm_idx); + } else { + tpc_ctl = TPC_MAX; + ath12k_warn(ar->ab, + "ctl array for tpc stats not received from fw\n"); + } + + rates_ctl_min = min_t(s16, rates, tpc_ctl); + + reg_pwr = tpc_stats->max_reg_allowed_power.reg_pwr_array[chain_idx]; + + if (reg_pwr < 0) + reg_pwr = TPC_INVAL; + + tpc = min_t(s16, rates_ctl_min, reg_pwr); + + /* MODULATION_LIMIT is the maximum power limit,tpc should not exceed + * modulation limit even if min tpc of all three array is greater + * modulation limit + */ + tpc = min_t(s16, tpc, MODULATION_LIMIT); + +out: + return tpc; +} + +static u16 ath12k_get_ratecode(u16 pream_idx, u16 nss, u16 mcs_rate) +{ + u16 mode_type = ~0; + + /* Below assignments are just for printing purpose only */ + switch (pream_idx) { + case WMI_TPC_PREAM_CCK: + mode_type = WMI_RATE_PREAMBLE_CCK; + break; + case WMI_TPC_PREAM_OFDM: + mode_type = WMI_RATE_PREAMBLE_OFDM; + break; + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_HT40: + mode_type = WMI_RATE_PREAMBLE_HT; + break; + case WMI_TPC_PREAM_VHT20: + case WMI_TPC_PREAM_VHT40: + case WMI_TPC_PREAM_VHT80: + case WMI_TPC_PREAM_VHT160: + mode_type = WMI_RATE_PREAMBLE_VHT; + break; + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_HE80: + case WMI_TPC_PREAM_HE160: + mode_type = WMI_RATE_PREAMBLE_HE; + break; + case WMI_TPC_PREAM_EHT20: + case WMI_TPC_PREAM_EHT40: + case WMI_TPC_PREAM_EHT60: + case WMI_TPC_PREAM_EHT80: + case WMI_TPC_PREAM_EHT120: + case WMI_TPC_PREAM_EHT140: + case WMI_TPC_PREAM_EHT160: + case WMI_TPC_PREAM_EHT200: + case WMI_TPC_PREAM_EHT240: + case WMI_TPC_PREAM_EHT280: + case WMI_TPC_PREAM_EHT320: + mode_type = WMI_RATE_PREAMBLE_EHT; + if (mcs_rate == 0 || mcs_rate == 1) + mcs_rate += 14; + else + mcs_rate -= 2; + break; + default: + return mode_type; + } + return ((mode_type << 8) | ((nss & 0x7) << 5) | (mcs_rate & 0x1F)); +} + +static bool ath12k_he_supports_extra_mcs(struct ath12k *ar, int freq) +{ + struct ath12k_pdev_cap *cap = &ar->pdev->cap; + struct ath12k_band_cap *cap_band; + bool extra_mcs_supported; + + if (freq <= ATH12K_2GHZ_MAX_FREQUENCY) + cap_band = &cap->band[NL80211_BAND_2GHZ]; + else if (freq <= ATH12K_5GHZ_MAX_FREQUENCY) + cap_band = &cap->band[NL80211_BAND_5GHZ]; + else + cap_band = &cap->band[NL80211_BAND_6GHZ]; + + extra_mcs_supported = u32_get_bits(cap_band->he_cap_info[1], + HE_EXTRA_MCS_SUPPORT); + return extra_mcs_supported; +} + +static int ath12k_tpc_fill_pream(struct ath12k *ar, char *buf, int buf_len, int len, + enum wmi_tpc_pream_bw pream_bw, u32 max_rix, + int max_nss, int max_rates, int pream_type, + enum wmi_halphy_ctrl_path_stats_id tpc_type, + int rate_idx, int eht_rate_idx) +{ + struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats; + int nss, rates, chains; + u8 active_tx_chains; + u16 rate_code; + s16 tpc; + + static const char *const pream_str[] = { + [WMI_TPC_PREAM_CCK] = "CCK", + [WMI_TPC_PREAM_OFDM] = "OFDM", + [WMI_TPC_PREAM_HT20] = "HT20", + [WMI_TPC_PREAM_HT40] = "HT40", + [WMI_TPC_PREAM_VHT20] = "VHT20", + [WMI_TPC_PREAM_VHT40] = "VHT40", + [WMI_TPC_PREAM_VHT80] = "VHT80", + [WMI_TPC_PREAM_VHT160] = "VHT160", + [WMI_TPC_PREAM_HE20] = "HE20", + [WMI_TPC_PREAM_HE40] = "HE40", + [WMI_TPC_PREAM_HE80] = "HE80", + [WMI_TPC_PREAM_HE160] = "HE160", + [WMI_TPC_PREAM_EHT20] = "EHT20", + [WMI_TPC_PREAM_EHT40] = "EHT40", + [WMI_TPC_PREAM_EHT60] = "EHT60", + [WMI_TPC_PREAM_EHT80] = "EHT80", + [WMI_TPC_PREAM_EHT120] = "EHT120", + [WMI_TPC_PREAM_EHT140] = "EHT140", + [WMI_TPC_PREAM_EHT160] = "EHT160", + [WMI_TPC_PREAM_EHT200] = "EHT200", + [WMI_TPC_PREAM_EHT240] = "EHT240", + [WMI_TPC_PREAM_EHT280] = "EHT280", + [WMI_TPC_PREAM_EHT320] = "EHT320"}; + + active_tx_chains = ar->num_tx_chains; + + for (nss = 0; nss < max_nss; nss++) { + for (rates = 0; rates < max_rates; rates++, rate_idx++, max_rix++) { + /* FW send extra MCS(10&11) for VHT and HE rates, + * this is not used. Hence skipping it here + */ + if (pream_type == WMI_RATE_PREAMBLE_VHT && + rates > ATH12K_VHT_MCS_MAX) + continue; + + if (pream_type == WMI_RATE_PREAMBLE_HE && + rates > ATH12K_HE_MCS_MAX) + continue; + + if (pream_type == WMI_RATE_PREAMBLE_EHT && + rates > ATH12K_EHT_MCS_MAX) + continue; + + rate_code = ath12k_get_ratecode(pream_bw, nss, rates); + len += scnprintf(buf + len, buf_len - len, + "%d\t %s\t 0x%03x\t", max_rix, + pream_str[pream_bw], rate_code); + + for (chains = 0; chains < active_tx_chains; chains++) { + if (nss > chains) { + len += scnprintf(buf + len, + buf_len - len, + "\t%s", "NA"); + } else { + tpc = ath12k_tpc_get_rate(ar, tpc_stats, + rate_idx, chains + 1, + rate_code, pream_bw, + tpc_type, + eht_rate_idx); + + if (tpc == TPC_INVAL) { + len += scnprintf(buf + len, + buf_len - len, "\tNA"); + } else { + len += scnprintf(buf + len, + buf_len - len, "\t%d", + tpc); + } + } + } + len += scnprintf(buf + len, buf_len - len, "\n"); + + if (pream_type == WMI_RATE_PREAMBLE_EHT) + /*For fetching the next eht rates pwr from rates array2*/ + ++eht_rate_idx; + } + } + + return len; +} + +static int ath12k_tpc_stats_print(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + char *buf, size_t len, + enum wmi_halphy_ctrl_path_stats_id type) +{ + u32 eht_idx = 0, pream_idx = 0, rate_pream_idx = 0, total_rates = 0, max_rix = 0; + u32 chan_freq, num_tx_chain, caps, i, j = 1; + size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE; + u8 nss, active_tx_chains; + bool he_ext_mcs; + static const char *const type_str[WMI_HALPHY_PDEV_TX_STATS_MAX] = { + [WMI_HALPHY_PDEV_TX_SU_STATS] = "SU", + [WMI_HALPHY_PDEV_TX_SUTXBF_STATS] = "SU WITH TXBF", + [WMI_HALPHY_PDEV_TX_MU_STATS] = "MU", + [WMI_HALPHY_PDEV_TX_MUTXBF_STATS] = "MU WITH TXBF"}; + + u8 max_rates[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = ATH12K_CCK_RATES, + [WMI_TPC_PREAM_OFDM] = ATH12K_OFDM_RATES, + [WMI_TPC_PREAM_HT20] = ATH12K_HT_RATES, + [WMI_TPC_PREAM_HT40] = ATH12K_HT_RATES, + [WMI_TPC_PREAM_VHT20] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT40] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT80] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT160] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_HE20] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE40] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE80] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE160] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_EHT20] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT40] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT60] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT80] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT120] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT140] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT160] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT200] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT240] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT280] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT320] = ATH12K_EHT_RATES}; + static const u8 max_nss[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = ATH12K_NSS_1, + [WMI_TPC_PREAM_OFDM] = ATH12K_NSS_1, + [WMI_TPC_PREAM_HT20] = ATH12K_NSS_4, + [WMI_TPC_PREAM_HT40] = ATH12K_NSS_4, + [WMI_TPC_PREAM_VHT20] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT40] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT80] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_HE20] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE40] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE80] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT20] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT40] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT60] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT80] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT120] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT140] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT200] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT240] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT280] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT320] = ATH12K_NSS_4}; + + u16 rate_idx[WMI_TPC_PREAM_MAX] = {}, eht_rate_idx[WMI_TPC_PREAM_MAX] = {}; + static const u8 pream_type[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = WMI_RATE_PREAMBLE_CCK, + [WMI_TPC_PREAM_OFDM] = WMI_RATE_PREAMBLE_OFDM, + [WMI_TPC_PREAM_HT20] = WMI_RATE_PREAMBLE_HT, + [WMI_TPC_PREAM_HT40] = WMI_RATE_PREAMBLE_HT, + [WMI_TPC_PREAM_VHT20] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT40] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT80] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT160] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_HE20] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE40] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE80] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE160] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_EHT20] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT40] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT60] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT80] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT120] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT140] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT160] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT200] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT240] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT280] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT320] = WMI_RATE_PREAMBLE_EHT}; + + chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq); + num_tx_chain = le32_to_cpu(tpc_stats->tpc_config.num_tx_chain); + caps = le32_to_cpu(tpc_stats->tpc_config.caps); + + active_tx_chains = ar->num_tx_chains; + he_ext_mcs = ath12k_he_supports_extra_mcs(ar, chan_freq); + + /* mcs 12&13 is sent by FW for certain HWs in rate array, skipping it as + * it is not supported + */ + if (he_ext_mcs) { + for (i = WMI_TPC_PREAM_HE20; i <= WMI_TPC_PREAM_HE160; ++i) + max_rates[i] = ATH12K_HE_RATES; + } + + if (type == WMI_HALPHY_PDEV_TX_MU_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) { + pream_idx = WMI_TPC_PREAM_VHT20; + + for (i = WMI_TPC_PREAM_CCK; i <= WMI_TPC_PREAM_HT40; ++i) + max_rix += max_nss[i] * max_rates[i]; + } + /* Enumerate all the rate indices */ + for (i = rate_pream_idx + 1; i < WMI_TPC_PREAM_MAX; i++) { + nss = (max_nss[i - 1] < num_tx_chain ? + max_nss[i - 1] : num_tx_chain); + + rate_idx[i] = rate_idx[i - 1] + max_rates[i - 1] * nss; + + if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) { + eht_rate_idx[j] = eht_rate_idx[j - 1] + max_rates[i] * nss; + ++j; + } + } + + for (i = 0; i < WMI_TPC_PREAM_MAX; i++) { + nss = (max_nss[i] < num_tx_chain ? + max_nss[i] : num_tx_chain); + total_rates += max_rates[i] * nss; + } + + len += scnprintf(buf + len, buf_len - len, + "No.of rates-%d\n", total_rates); + + len += scnprintf(buf + len, buf_len - len, + "**************** %s ****************\n", + type_str[type]); + len += scnprintf(buf + len, buf_len - len, + "\t\t\t\tTPC values for Active chains\n"); + len += scnprintf(buf + len, buf_len - len, + "Rate idx Preamble Rate code"); + + for (i = 1; i <= active_tx_chains; ++i) { + len += scnprintf(buf + len, buf_len - len, + "\t%d-Chain", i); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = pream_idx; i < WMI_TPC_PREAM_MAX; i++) { + if (chan_freq <= 2483) { + if (i == WMI_TPC_PREAM_VHT80 || + i == WMI_TPC_PREAM_VHT160 || + i == WMI_TPC_PREAM_HE80 || + i == WMI_TPC_PREAM_HE160 || + (i >= WMI_TPC_PREAM_EHT60 && + i <= WMI_TPC_PREAM_EHT320)) { + max_rix += max_nss[i] * max_rates[i]; + continue; + } + } else { + if (i == WMI_TPC_PREAM_CCK) { + max_rix += max_rates[i]; + continue; + } + } + + nss = (max_nss[i] < ar->num_tx_chains ? max_nss[i] : ar->num_tx_chains); + + if (!(caps & + (1 << ATH12K_TPC_STATS_SUPPORT_BE_PUNC))) { + if (i == WMI_TPC_PREAM_EHT60 || i == WMI_TPC_PREAM_EHT120 || + i == WMI_TPC_PREAM_EHT140 || i == WMI_TPC_PREAM_EHT200 || + i == WMI_TPC_PREAM_EHT240 || i == WMI_TPC_PREAM_EHT280) { + max_rix += max_nss[i] * max_rates[i]; + continue; + } + } + + len = ath12k_tpc_fill_pream(ar, buf, buf_len, len, i, max_rix, nss, + max_rates[i], pream_type[i], + type, rate_idx[i], eht_rate_idx[eht_idx]); + + if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) + /*For fetch the next index eht rates from rates array2*/ + ++eht_idx; + + max_rix += max_nss[i] * max_rates[i]; + } + return len; +} + +static void ath12k_tpc_stats_fill(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + char *buf) +{ + size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE; + struct wmi_tpc_config_params *tpc; + size_t len = 0; + + if (!tpc_stats) { + ath12k_warn(ar->ab, "failed to find tpc stats\n"); + return; + } + + spin_lock_bh(&ar->data_lock); + + tpc = &tpc_stats->tpc_config; + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, + "*************** TPC config **************\n"); + len += scnprintf(buf + len, buf_len - len, + "* powers are in 0.25 dBm steps\n"); + len += scnprintf(buf + len, buf_len - len, + "reg domain-%d\t\tchan freq-%d\n", + tpc->reg_domain, tpc->chan_freq); + len += scnprintf(buf + len, buf_len - len, + "power limit-%d\t\tmax reg-domain Power-%d\n", + le32_to_cpu(tpc->twice_max_reg_power) / 2, tpc->power_limit); + len += scnprintf(buf + len, buf_len - len, + "No.of tx chain-%d\t", + ar->num_tx_chains); + + ath12k_tpc_stats_print(ar, tpc_stats, buf, len, + ar->debug.tpc_stats_type); + + spin_unlock_bh(&ar->data_lock); +} + +static int ath12k_open_tpc_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah->state != ATH12K_HW_STATE_ON) { + ath12k_warn(ar->ab, "Interface not up\n"); + return -ENETDOWN; + } + + void *buf __free(kfree) = kzalloc(ATH12K_TPC_STATS_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ath12k_debug_tpc_stats_request(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to request tpc stats: %d\n", + ret); + return ret; + } + + if (!wait_for_completion_timeout(&ar->debug.tpc_complete, TPC_STATS_WAIT_TIME)) { + spin_lock_bh(&ar->data_lock); + ath12k_wmi_free_tpc_stats_mem(ar); + ar->debug.tpc_request = false; + spin_unlock_bh(&ar->data_lock); + return -ETIMEDOUT; + } + + ath12k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf); + file->private_data = no_free_ptr(buf); + + spin_lock_bh(&ar->data_lock); + ath12k_wmi_free_tpc_stats_mem(ar); + spin_unlock_bh(&ar->data_lock); + + return 0; +} + +static ssize_t ath12k_read_tpc_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static int ath12k_release_tpc_stats(struct inode *inode, + struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static const struct file_operations fops_tpc_stats = { + .open = ath12k_open_tpc_stats, + .release = ath12k_release_tpc_stats, + .read = ath12k_read_tpc_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static const struct file_operations fops_tpc_stats_type = { + .write = ath12k_write_tpc_stats_type, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t ath12k_write_extd_rx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 ring_id, rx_filter = 0; + bool enable; + int ret, i; + + if (kstrtobool_from_user(ubuf, count, &enable)) + return -EINVAL; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + + if (!ar->ab->hw_params->rxdma1_enable) { + ret = count; + goto exit; + } + + if (ar->ah->state != ATH12K_HW_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable == ar->debug.extd_rx_stats) { + ret = count; + goto exit; + } + + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO; + + tlv_filter.rx_filter = rx_filter; + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath12k_mac_mon_status_filter_default; + } + + ar->debug.rx_filter = tlv_filter.rx_filter; + + for (i = 0; i < ar->ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto exit; + } + } + + ar->debug.extd_rx_stats = !!enable; + ret = count; +exit: + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + return ret; +} + +static ssize_t ath12k_read_extd_rx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + char buf[32]; + int len = 0; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.extd_rx_stats); + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_rx_stats = { + .read = ath12k_read_extd_rx_stats, + .write = ath12k_write_extd_rx_stats, + .open = simple_open, +}; + +static int ath12k_open_link_stats(struct inode *inode, struct file *file) +{ + struct ath12k_vif *ahvif = inode->i_private; + size_t len = 0, buf_len = (PAGE_SIZE * 2); + struct ath12k_link_stats linkstat; + struct ath12k_link_vif *arvif; + unsigned long links_map; + struct wiphy *wiphy; + int link_id, i; + char *buf; + + if (!ahvif) + return -EINVAL; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + wiphy = ahvif->ah->hw->wiphy; + wiphy_lock(wiphy); + + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, + IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = rcu_dereference_protected(ahvif->link[link_id], + lockdep_is_held(&wiphy->mtx)); + + spin_lock_bh(&arvif->link_stats_lock); + linkstat = arvif->link_stats; + spin_unlock_bh(&arvif->link_stats_lock); + + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Unicast Frames Enqueued = %d\n", + link_id, linkstat.tx_enqueued); + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Broadcast Frames Enqueued = %d\n", + link_id, linkstat.tx_bcast_mcast); + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Frames Completed = %d\n", + link_id, linkstat.tx_completed); + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Frames Dropped = %d\n", + link_id, linkstat.tx_dropped); + + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Frame descriptor Encap Type = ", + link_id); + + len += scnprintf(buf + len, buf_len - len, + " raw:%d", + linkstat.tx_encap_type[0]); + + len += scnprintf(buf + len, buf_len - len, + " native_wifi:%d", + linkstat.tx_encap_type[1]); + + len += scnprintf(buf + len, buf_len - len, + " ethernet:%d", + linkstat.tx_encap_type[2]); + + len += scnprintf(buf + len, buf_len - len, + "\nlink[%d] Tx Frame descriptor Encrypt Type = ", + link_id); + + for (i = 0; i < HAL_ENCRYPT_TYPE_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + " %d:%d", i, + linkstat.tx_encrypt_type[i]); + } + len += scnprintf(buf + len, buf_len - len, + "\nlink[%d] Tx Frame descriptor Type = buffer:%d extension:%d\n", + link_id, linkstat.tx_desc_type[0], + linkstat.tx_desc_type[1]); + + len += scnprintf(buf + len, buf_len - len, + "------------------------------------------------------\n"); + } + + wiphy_unlock(wiphy); + + file->private_data = buf; + + return 0; +} + +static int ath12k_release_link_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static ssize_t ath12k_read_link_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations ath12k_fops_link_stats = { + .open = ath12k_open_link_stats, + .release = ath12k_release_link_stats, + .read = ath12k_read_link_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + + debugfs_create_file("link_stats", 0400, vif->debugfs_dir, ahvif, + &ath12k_fops_link_stats); +} + +static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k_base *ab = file->private_data; + struct ath12k_device_dp_stats *device_stats = &ab->device_stats; + int len = 0, i, j, ret; + struct ath12k *ar; + const int size = 4096; + static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { + [HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR] = "Overflow", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR] = "MPDU len", + [HAL_REO_ENTR_RING_RXDMA_ECODE_FCS_ERR] = "FCS", + [HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR] = "Decrypt", + [HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR] = "TKIP MIC", + [HAL_REO_ENTR_RING_RXDMA_ECODE_UNECRYPTED_ERR] = "Unencrypt", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LEN_ERR] = "MSDU len", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LIMIT_ERR] = "MSDU limit", + [HAL_REO_ENTR_RING_RXDMA_ECODE_WIFI_PARSE_ERR] = "WiFi parse", + [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_PARSE_ERR] = "AMSDU parse", + [HAL_REO_ENTR_RING_RXDMA_ECODE_SA_TIMEOUT_ERR] = "SA timeout", + [HAL_REO_ENTR_RING_RXDMA_ECODE_DA_TIMEOUT_ERR] = "DA timeout", + [HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR] = "Flow timeout", + [HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR] = "Flush req", + [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_FRAG_ERR] = "AMSDU frag", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MULTICAST_ECHO_ERR] = "Multicast echo", + [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_MISMATCH_ERR] = "AMSDU mismatch", + [HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR] = "Unauth WDS", + [HAL_REO_ENTR_RING_RXDMA_ECODE_GRPCAST_AMSDU_WDS_ERR] = "AMSDU or WDS"}; + + static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = { + [HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO] = "Desc addr zero", + [HAL_REO_DEST_RING_ERROR_CODE_DESC_INVALID] = "Desc inval", + [HAL_REO_DEST_RING_ERROR_CODE_AMPDU_IN_NON_BA] = "AMPDU in non BA", + [HAL_REO_DEST_RING_ERROR_CODE_NON_BA_DUPLICATE] = "Non BA dup", + [HAL_REO_DEST_RING_ERROR_CODE_BA_DUPLICATE] = "BA dup", + [HAL_REO_DEST_RING_ERROR_CODE_FRAME_2K_JUMP] = "Frame 2k jump", + [HAL_REO_DEST_RING_ERROR_CODE_BAR_2K_JUMP] = "BAR 2k jump", + [HAL_REO_DEST_RING_ERROR_CODE_FRAME_OOR] = "Frame OOR", + [HAL_REO_DEST_RING_ERROR_CODE_BAR_OOR] = "BAR OOR", + [HAL_REO_DEST_RING_ERROR_CODE_NO_BA_SESSION] = "No BA session", + [HAL_REO_DEST_RING_ERROR_CODE_FRAME_SN_EQUALS_SSN] = "Frame SN equal SSN", + [HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED] = "PN check fail", + [HAL_REO_DEST_RING_ERROR_CODE_2K_ERR_FLAG_SET] = "2k err", + [HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET] = "PN err", + [HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED] = "Desc blocked"}; + + static const char *wbm_rel_src[HAL_WBM_REL_SRC_MODULE_MAX] = { + [HAL_WBM_REL_SRC_MODULE_TQM] = "TQM", + [HAL_WBM_REL_SRC_MODULE_RXDMA] = "Rxdma", + [HAL_WBM_REL_SRC_MODULE_REO] = "Reo", + [HAL_WBM_REL_SRC_MODULE_FW] = "FW", + [HAL_WBM_REL_SRC_MODULE_SW] = "SW"}; + + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "DEVICE RX STATS:\n\n"); + len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", + device_stats->err_ring_pkts); + len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n", + device_stats->invalid_rbm); + len += scnprintf(buf + len, size - len, "RXDMA errors:\n"); + + for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + rxdma_err[i], device_stats->rxdma_error[i]); + + len += scnprintf(buf + len, size - len, "\nREO errors:\n"); + + for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + reo_err[i], device_stats->reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); + + for (i = 0; i < DP_REO_DST_RING_MAX; i++) + len += scnprintf(buf + len, size - len, + "ring%d: %u\n", i, + device_stats->hal_reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nDEVICE TX STATS:\n"); + len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n"); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + len += scnprintf(buf + len, size - len, "ring%d: %u\n", + i, device_stats->tx_err.desc_na[i]); + + len += scnprintf(buf + len, size - len, + "\nMisc Transmit Failures: %d\n", + atomic_read(&device_stats->tx_err.misc_fail)); + + len += scnprintf(buf + len, size - len, "\ntx_wbm_rel_source:"); + + for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->tx_wbm_rel_source[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\ntqm_rel_reason:"); + + for (i = 0; i < MAX_TQM_RELEASE_REASON; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->tqm_rel_reason[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\nfw_tx_status:"); + + for (i = 0; i < MAX_FW_TX_STATUS; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->fw_tx_status[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\ntx_enqueued:"); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + len += scnprintf(buf + len, size - len, " %d:%u", i, + device_stats->tx_enqueued[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\ntx_completed:"); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->tx_completed[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + for (i = 0; i < ab->num_radios; i++) { + ar = ath12k_mac_get_ar_by_pdev_id(ab, DP_SW2HW_MACID(i)); + if (ar) { + len += scnprintf(buf + len, size - len, + "\nradio%d tx_pending: %u\n", i, + atomic_read(&ar->dp.num_tx_pending)); + } + } + + len += scnprintf(buf + len, size - len, "\nREO Rx Received:\n"); + + for (i = 0; i < DP_REO_DST_RING_MAX; i++) { + len += scnprintf(buf + len, size - len, "Ring%d:", i + 1); + + for (j = 0; j < ATH12K_MAX_DEVICES; j++) { + len += scnprintf(buf + len, size - len, + "\t%d:%u", j, + device_stats->reo_rx[i][j]); + } + + len += scnprintf(buf + len, size - len, "\n"); + } + + len += scnprintf(buf + len, size - len, "\nRx WBM REL SRC Errors:\n"); + + for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) { + len += scnprintf(buf + len, size - len, "%s:", wbm_rel_src[i]); + + for (j = 0; j < ATH12K_MAX_DEVICES; j++) { + len += scnprintf(buf + len, + size - len, + "\t%d:%u", j, + device_stats->rx_wbm_rel_source[i][j]); + } + + len += scnprintf(buf + len, size - len, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + return ret; +} + +static const struct file_operations fops_device_dp_stats = { + .read = ath12k_debugfs_dump_device_dp_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_pdev_create(struct ath12k_base *ab) +{ + debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, + &fops_simulate_fw_crash); + + debugfs_create_file("device_dp_stats", 0400, ab->debugfs_soc, ab, + &fops_device_dp_stats); +} + +void ath12k_debugfs_soc_create(struct ath12k_base *ab) +{ + bool dput_needed; + char soc_name[64] = { 0 }; + struct dentry *debugfs_ath12k; + + debugfs_ath12k = debugfs_lookup("ath12k", NULL); + if (debugfs_ath12k) { + /* a dentry from lookup() needs dput() after we don't use it */ + dput_needed = true; + } else { + debugfs_ath12k = debugfs_create_dir("ath12k", NULL); + if (IS_ERR_OR_NULL(debugfs_ath12k)) + return; + dput_needed = false; + } + + scnprintf(soc_name, sizeof(soc_name), "%s-%s", ath12k_bus_str(ab->hif.bus), + dev_name(ab->dev)); + + ab->debugfs_soc = debugfs_create_dir(soc_name, debugfs_ath12k); + + if (dput_needed) + dput(debugfs_ath12k); +} + +void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_soc); + ab->debugfs_soc = NULL; + /* We are not removing ath12k directory on purpose, even if it + * would be empty. This simplifies the directory handling and it's + * a minor cosmetic issue to leave an empty ath12k directory to + * debugfs. + */ +} + +static int ath12k_open_vdev_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_fw_stats_req_params param; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (!ah) + return -ENETDOWN; + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + /* VDEV stats is always sent for all active VDEVs from FW */ + param.vdev_id = 0; + param.stats_id = WMI_REQUEST_VDEV_STAT; + + ret = ath12k_mac_get_fw_stats(ar, ¶m); + if (ret) { + ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); + return ret; + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_vdev_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_vdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_vdev_stats = { + .open = ath12k_open_vdev_stats, + .release = ath12k_release_vdev_stats, + .read = ath12k_read_vdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath12k_open_bcn_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_link_vif *arvif; + struct ath12k_fw_stats_req_params param; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah && ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + param.stats_id = WMI_REQUEST_BCN_STAT; + + /* loop all active VDEVs for bcn stats */ + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_up) + continue; + + param.vdev_id = arvif->vdev_id; + ret = ath12k_mac_get_fw_stats(ar, ¶m); + if (ret) { + ath12k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); + return ret; + } + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + /* since beacon stats request is looped for all active VDEVs, saved fw + * stats is not freed for each request until done for all active VDEVs + */ + spin_lock_bh(&ar->data_lock); + ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn); + spin_unlock_bh(&ar->data_lock); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_bcn_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_bcn_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bcn_stats = { + .open = ath12k_open_bcn_stats, + .release = ath12k_release_bcn_stats, + .read = ath12k_read_bcn_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath12k_open_pdev_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + struct ath12k_base *ab = ar->ab; + struct ath12k_fw_stats_req_params param; + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah && ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + param.vdev_id = 0; + param.stats_id = WMI_REQUEST_PDEV_STAT; + + ret = ath12k_mac_get_fw_stats(ar, ¶m); + if (ret) { + ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + return ret; + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_pdev_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_pdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pdev_stats = { + .open = ath12k_open_pdev_stats, + .release = ath12k_release_pdev_stats, + .read = ath12k_read_pdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static +void ath12k_debugfs_fw_stats_register(struct ath12k *ar) +{ + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", + ar->debug.debugfs_pdev); + + /* all stats debugfs files created are under "fw_stats" directory + * created per PDEV + */ + debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, + &fops_vdev_stats); + debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, + &fops_bcn_stats); + debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, + &fops_pdev_stats); + + ath12k_fw_stats_init(ar); +} + +void ath12k_debugfs_register(struct ath12k *ar) +{ + struct ath12k_base *ab = ar->ab; + struct ieee80211_hw *hw = ar->ah->hw; + char pdev_name[5]; + char buf[100] = {0}; + + scnprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); + + ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); + + /* Create a symlink under ieee80211/phy* */ + scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", ar->debug.debugfs_pdev); + ar->debug.debugfs_pdev_symlink = debugfs_create_symlink("ath12k", + hw->wiphy->debugfsdir, + buf); + + if (ar->mac.sbands[NL80211_BAND_5GHZ].channels) { + debugfs_create_file("dfs_simulate_radar", 0200, + ar->debug.debugfs_pdev, ar, + &fops_simulate_radar); + } + + debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_pdev, ar, + &fops_tpc_stats); + debugfs_create_file("tpc_stats_type", 0200, ar->debug.debugfs_pdev, + ar, &fops_tpc_stats_type); + init_completion(&ar->debug.tpc_complete); + + ath12k_debugfs_htt_stats_register(ar); + ath12k_debugfs_fw_stats_register(ar); + + debugfs_create_file("ext_rx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_rx_stats); +} + +void ath12k_debugfs_unregister(struct ath12k *ar) +{ + if (!ar->debug.debugfs_pdev) + return; + + /* Remove symlink under ieee80211/phy* */ + debugfs_remove(ar->debug.debugfs_pdev_symlink); + debugfs_remove_recursive(ar->debug.debugfs_pdev); + ar->debug.debugfs_pdev_symlink = NULL; + ar->debug.debugfs_pdev = NULL; +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h new file mode 100644 index 000000000000..21641a8a0346 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _ATH12K_DEBUGFS_H_ +#define _ATH12K_DEBUGFS_H_ + +#ifdef CONFIG_ATH12K_DEBUGFS +void ath12k_debugfs_soc_create(struct ath12k_base *ab); +void ath12k_debugfs_soc_destroy(struct ath12k_base *ab); +void ath12k_debugfs_register(struct ath12k *ar); +void ath12k_debugfs_unregister(struct ath12k *ar); +void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void ath12k_debugfs_pdev_create(struct ath12k_base *ab); + +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) +{ + return ar->debug.extd_rx_stats; +} + +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar) +{ + return ar->debug.rx_filter; +} + +#define ATH12K_CCK_RATES 4 +#define ATH12K_OFDM_RATES 8 +#define ATH12K_HT_RATES 8 +#define ATH12K_VHT_RATES 12 +#define ATH12K_HE_RATES 12 +#define ATH12K_HE_RATES_WITH_EXTRA_MCS 14 +#define ATH12K_EHT_RATES 16 +#define HE_EXTRA_MCS_SUPPORT GENMASK(31, 16) +#define ATH12K_NSS_1 1 +#define ATH12K_NSS_4 4 +#define ATH12K_NSS_8 8 +#define ATH12K_HW_NSS(_rcode) (((_rcode) >> 5) & 0x7) +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define MAX_TPC_PREAM_STR_LEN 7 +#define TPC_INVAL -128 +#define TPC_MAX 127 +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define TPC_STATS_TOT_ROW 700 +#define TPC_STATS_TOT_COLUMN 100 +#define MODULATION_LIMIT 126 + +#define ATH12K_TPC_STATS_BUF_SIZE (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN) + +enum wmi_tpc_pream_bw { + WMI_TPC_PREAM_CCK, + WMI_TPC_PREAM_OFDM, + WMI_TPC_PREAM_HT20, + WMI_TPC_PREAM_HT40, + WMI_TPC_PREAM_VHT20, + WMI_TPC_PREAM_VHT40, + WMI_TPC_PREAM_VHT80, + WMI_TPC_PREAM_VHT160, + WMI_TPC_PREAM_HE20, + WMI_TPC_PREAM_HE40, + WMI_TPC_PREAM_HE80, + WMI_TPC_PREAM_HE160, + WMI_TPC_PREAM_EHT20, + WMI_TPC_PREAM_EHT40, + WMI_TPC_PREAM_EHT60, + WMI_TPC_PREAM_EHT80, + WMI_TPC_PREAM_EHT120, + WMI_TPC_PREAM_EHT140, + WMI_TPC_PREAM_EHT160, + WMI_TPC_PREAM_EHT200, + WMI_TPC_PREAM_EHT240, + WMI_TPC_PREAM_EHT280, + WMI_TPC_PREAM_EHT320, + WMI_TPC_PREAM_MAX +}; + +enum ath12k_debug_tpc_stats_ctl_mode { + ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ, + + ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20 = 23, + ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120 +}; + +enum ath12k_debug_tpc_stats_support_modes { + ATH12K_TPC_STATS_SUPPORT_160 = 0, + ATH12K_TPC_STATS_SUPPORT_320, + ATH12K_TPC_STATS_SUPPORT_AX, + ATH12K_TPC_STATS_SUPPORT_AX_EXTRA_MCS, + ATH12K_TPC_STATS_SUPPORT_BE, + ATH12K_TPC_STATS_SUPPORT_BE_PUNC, +}; +#else +static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) +{ +} + +static inline void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) +{ +} + +static inline void ath12k_debugfs_register(struct ath12k *ar) +{ +} + +static inline void ath12k_debugfs_unregister(struct ath12k *ar) +{ +} + +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) +{ + return false; +} + +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar) +{ + return 0; +} + +static inline void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ +} + +static inline void ath12k_debugfs_pdev_create(struct ath12k_base *ab) +{ +} +#endif /* CONFIG_ATH12K_DEBUGFS */ + +#endif /* _ATH12K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c new file mode 100644 index 000000000000..aeaf970339d4 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -0,0 +1,5628 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/vmalloc.h> +#include "core.h" +#include "debug.h" +#include "debugfs_htt_stats.h" +#include "dp_tx.h" +#include "dp_rx.h" + +static u32 +print_array_to_buf_index(u8 *buf, u32 offset, const char *header, u32 stats_index, + const __le32 *array, u32 array_len, const char *footer) +{ + int index = 0; + u8 i; + + if (header) { + index += scnprintf(buf + offset, + ATH12K_HTT_STATS_BUF_SIZE - offset, + "%s = ", header); + } + for (i = 0; i < array_len; i++) { + index += scnprintf(buf + offset + index, + (ATH12K_HTT_STATS_BUF_SIZE - offset) - index, + " %u:%u,", stats_index++, le32_to_cpu(array[i])); + } + /* To overwrite the last trailing comma */ + index--; + *(buf + offset + index) = '\0'; + + if (footer) { + index += scnprintf(buf + offset + index, + (ATH12K_HTT_STATS_BUF_SIZE - offset) - index, + "%s", footer); + } + return index; +} + +static u32 +print_array_to_buf(u8 *buf, u32 offset, const char *header, + const __le32 *array, u32 array_len, const char *footer) +{ + return print_array_to_buf_index(buf, offset, header, 0, array, array_len, + footer); +} + +static u32 +print_array_to_buf_s8(u8 *buf, u32 offset, const char *header, u32 stats_index, + const s8 *array, u32 array_len, const char *footer) +{ + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + int index = 0; + u8 i; + + if (header) + index += scnprintf(buf + offset, buf_len - offset, "%s = ", header); + + for (i = 0; i < array_len; i++) { + index += scnprintf(buf + offset + index, (buf_len - offset) - index, + " %u:%d,", stats_index++, array[i]); + } + + index--; + if ((offset + index) < buf_len) + buf[offset + index] = '\0'; + + if (footer) { + index += scnprintf(buf + offset + index, (buf_len - offset) - index, + "%s", footer); + } + + return index; +} + +static const char *ath12k_htt_ax_tx_rx_ru_size_to_str(u8 ru_size) +{ + switch (ru_size) { + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26: + return "26"; + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52: + return "52"; + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106: + return "106"; + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242: + return "242"; + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484: + return "484"; + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996: + return "996"; + case ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2: + return "996x2"; + default: + return "unknown"; + } +} + +static const char *ath12k_htt_be_tx_rx_ru_size_to_str(u8 ru_size) +{ + switch (ru_size) { + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_26: + return "26"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_52: + return "52"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_52_26: + return "52+26"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_106: + return "106"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_106_26: + return "106+26"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_242: + return "242"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_484: + return "484"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_484_242: + return "484+242"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996: + return "996"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996_484: + return "996+484"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996_484_242: + return "996+484+242"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x2: + return "996x2"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x2_484: + return "996x2+484"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x3: + return "996x3"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x3_484: + return "996x3+484"; + case ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x4: + return "996x4"; + default: + return "unknown"; + } +} + +static const char* +ath12k_tx_ru_size_to_str(enum ath12k_htt_stats_ru_type ru_type, u8 ru_size) +{ + if (ru_type == ATH12K_HTT_STATS_RU_TYPE_SINGLE_RU_ONLY) + return ath12k_htt_ax_tx_rx_ru_size_to_str(ru_size); + else if (ru_type == ATH12K_HTT_STATS_RU_TYPE_SINGLE_AND_MULTI_RU) + return ath12k_htt_be_tx_rx_ru_size_to_str(ru_size); + else + return "unknown"; +} + +static void +htt_print_tx_pdev_stats_cmn_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "comp_delivered = %u\n", + le32_to_cpu(htt_stats_buf->comp_delivered)); + len += scnprintf(buf + len, buf_len - len, "self_triggers = %u\n", + le32_to_cpu(htt_stats_buf->self_triggers)); + len += scnprintf(buf + len, buf_len - len, "hw_queued = %u\n", + le32_to_cpu(htt_stats_buf->hw_queued)); + len += scnprintf(buf + len, buf_len - len, "hw_reaped = %u\n", + le32_to_cpu(htt_stats_buf->hw_reaped)); + len += scnprintf(buf + len, buf_len - len, "underrun = %u\n", + le32_to_cpu(htt_stats_buf->underrun)); + len += scnprintf(buf + len, buf_len - len, "hw_paused = %u\n", + le32_to_cpu(htt_stats_buf->hw_paused)); + len += scnprintf(buf + len, buf_len - len, "hw_flush = %u\n", + le32_to_cpu(htt_stats_buf->hw_flush)); + len += scnprintf(buf + len, buf_len - len, "hw_filt = %u\n", + le32_to_cpu(htt_stats_buf->hw_filt)); + len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", + le32_to_cpu(htt_stats_buf->tx_abort)); + len += scnprintf(buf + len, buf_len - len, "ppdu_ok = %u\n", + le32_to_cpu(htt_stats_buf->ppdu_ok)); + len += scnprintf(buf + len, buf_len - len, "mpdu_requeued = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_requed)); + len += scnprintf(buf + len, buf_len - len, "tx_xretry = %u\n", + le32_to_cpu(htt_stats_buf->tx_xretry)); + len += scnprintf(buf + len, buf_len - len, "data_rc = %u\n", + le32_to_cpu(htt_stats_buf->data_rc)); + len += scnprintf(buf + len, buf_len - len, "mpdu_dropped_xretry = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_dropped_xretry)); + len += scnprintf(buf + len, buf_len - len, "illegal_rate_phy_err = %u\n", + le32_to_cpu(htt_stats_buf->illgl_rate_phy_err)); + len += scnprintf(buf + len, buf_len - len, "cont_xretry = %u\n", + le32_to_cpu(htt_stats_buf->cont_xretry)); + len += scnprintf(buf + len, buf_len - len, "tx_timeout = %u\n", + le32_to_cpu(htt_stats_buf->tx_timeout)); + len += scnprintf(buf + len, buf_len - len, "tx_time_dur_data = %u\n", + le32_to_cpu(htt_stats_buf->tx_time_dur_data)); + len += scnprintf(buf + len, buf_len - len, "pdev_resets = %u\n", + le32_to_cpu(htt_stats_buf->pdev_resets)); + len += scnprintf(buf + len, buf_len - len, "phy_underrun = %u\n", + le32_to_cpu(htt_stats_buf->phy_underrun)); + len += scnprintf(buf + len, buf_len - len, "txop_ovf = %u\n", + le32_to_cpu(htt_stats_buf->txop_ovf)); + len += scnprintf(buf + len, buf_len - len, "seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->seq_posted)); + len += scnprintf(buf + len, buf_len - len, "seq_failed_queueing = %u\n", + le32_to_cpu(htt_stats_buf->seq_failed_queueing)); + len += scnprintf(buf + len, buf_len - len, "seq_completed = %u\n", + le32_to_cpu(htt_stats_buf->seq_completed)); + len += scnprintf(buf + len, buf_len - len, "seq_restarted = %u\n", + le32_to_cpu(htt_stats_buf->seq_restarted)); + len += scnprintf(buf + len, buf_len - len, "seq_txop_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->seq_txop_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "next_seq_cancel = %u\n", + le32_to_cpu(htt_stats_buf->next_seq_cancel)); + len += scnprintf(buf + len, buf_len - len, "dl_mu_mimo_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->mu_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "dl_mu_ofdma_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->mu_ofdma_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "ul_mu_mimo_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "ul_mu_ofdma_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "mu_mimo_peer_blacklisted = %u\n", + le32_to_cpu(htt_stats_buf->num_mu_peer_blacklisted)); + len += scnprintf(buf + len, buf_len - len, "seq_qdepth_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->seq_qdepth_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "seq_min_msdu_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->seq_min_msdu_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "mu_seq_min_msdu_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->mu_seq_min_msdu_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "seq_switch_hw_paused = %u\n", + le32_to_cpu(htt_stats_buf->seq_switch_hw_paused)); + len += scnprintf(buf + len, buf_len - len, "next_seq_posted_dsr = %u\n", + le32_to_cpu(htt_stats_buf->next_seq_posted_dsr)); + len += scnprintf(buf + len, buf_len - len, "seq_posted_isr = %u\n", + le32_to_cpu(htt_stats_buf->seq_posted_isr)); + len += scnprintf(buf + len, buf_len - len, "seq_ctrl_cached = %u\n", + le32_to_cpu(htt_stats_buf->seq_ctrl_cached)); + len += scnprintf(buf + len, buf_len - len, "mpdu_count_tqm = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_count_tqm)); + len += scnprintf(buf + len, buf_len - len, "msdu_count_tqm = %u\n", + le32_to_cpu(htt_stats_buf->msdu_count_tqm)); + len += scnprintf(buf + len, buf_len - len, "mpdu_removed_tqm = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_removed_tqm)); + len += scnprintf(buf + len, buf_len - len, "msdu_removed_tqm = %u\n", + le32_to_cpu(htt_stats_buf->msdu_removed_tqm)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdus_max_retries = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdus_max_retries)); + len += scnprintf(buf + len, buf_len - len, "mpdus_sw_flush = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_sw_flush)); + len += scnprintf(buf + len, buf_len - len, "mpdus_hw_filter = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_hw_filter)); + len += scnprintf(buf + len, buf_len - len, "mpdus_truncated = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_truncated)); + len += scnprintf(buf + len, buf_len - len, "mpdus_ack_failed = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_ack_failed)); + len += scnprintf(buf + len, buf_len - len, "mpdus_expired = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_expired)); + len += scnprintf(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_seq_hw_retry)); + len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", + le32_to_cpu(htt_stats_buf->ack_tlv_proc)); + len += scnprintf(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u\n", + le32_to_cpu(htt_stats_buf->coex_abort_mpdu_cnt_valid)); + len += scnprintf(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->coex_abort_mpdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u\n", + le32_to_cpu(htt_stats_buf->num_total_ppdus_tried_ota)); + len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u\n", + le32_to_cpu(htt_stats_buf->num_data_ppdus_tried_ota)); + len += scnprintf(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u\n", + le32_to_cpu(htt_stats_buf->local_ctrl_mgmt_enqued)); + len += scnprintf(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u\n", + le32_to_cpu(htt_stats_buf->local_ctrl_mgmt_freed)); + len += scnprintf(buf + len, buf_len - len, "local_data_enqued = %u\n", + le32_to_cpu(htt_stats_buf->local_data_enqued)); + len += scnprintf(buf + len, buf_len - len, "local_data_freed = %u\n", + le32_to_cpu(htt_stats_buf->local_data_freed)); + len += scnprintf(buf + len, buf_len - len, "mpdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_tried)); + len += scnprintf(buf + len, buf_len - len, "isr_wait_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->isr_wait_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "tx_active_dur_us_low = %u\n", + le32_to_cpu(htt_stats_buf->tx_active_dur_us_low)); + len += scnprintf(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n", + le32_to_cpu(htt_stats_buf->tx_active_dur_us_high)); + len += scnprintf(buf + len, buf_len - len, "fes_offsets_err_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->fes_offsets_err_cnt)); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_urrn_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_urrn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_PDEV_MAX_URRN_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_URRN_TLV:\n"); + + len += print_array_to_buf(buf, len, "urrn_stats", htt_stats_buf->urrn_stats, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_flush_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_flush_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_FLUSH_TLV:\n"); + + len += print_array_to_buf(buf, len, "flush_errs", htt_stats_buf->flush_errs, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_sifs_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_sifs_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_TLV:\n"); + + len += print_array_to_buf(buf, len, "sifs_status", htt_stats_buf->sifs_status, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_mu_ppdu_dist_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv *htt_stats_buf = tag_buf; + char *mode; + u8 j, hw_mode, i, str_buf_len; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 stats_value; + u8 max_ppdu = ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST; + u8 max_sched = ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS; + char str_buf[ATH12K_HTT_MAX_STRING_LEN]; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + hw_mode = le32_to_cpu(htt_stats_buf->hw_mode); + + switch (hw_mode) { + case ATH12K_HTT_STATS_HWMODE_AC: + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_AC_MU_PPDU_DISTRIBUTION_STATS:\n"); + mode = "ac"; + break; + case ATH12K_HTT_STATS_HWMODE_AX: + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_AX_MU_PPDU_DISTRIBUTION_STATS:\n"); + mode = "ax"; + break; + case ATH12K_HTT_STATS_HWMODE_BE: + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_BE_MU_PPDU_DISTRIBUTION_STATS:\n"); + mode = "be"; + break; + default: + return; + } + + for (i = 0; i < ATH12K_HTT_STATS_NUM_NR_BINS ; i++) { + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_seq_posted_nr%u = %u\n", mode, + ((i + 1) * 4), htt_stats_buf->num_seq_posted[i]); + str_buf_len = 0; + memset(str_buf, 0x0, sizeof(str_buf)); + for (j = 0; j < ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST ; j++) { + stats_value = le32_to_cpu(htt_stats_buf->num_ppdu_posted_per_burst + [i * max_ppdu + j]); + str_buf_len += scnprintf(&str_buf[str_buf_len], + ATH12K_HTT_MAX_STRING_LEN - str_buf_len, + " %u:%u,", j, stats_value); + } + /* To overwrite the last trailing comma */ + str_buf[str_buf_len - 1] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_ppdu_posted_per_burst_nr%u = %s\n", + mode, ((i + 1) * 4), str_buf); + str_buf_len = 0; + memset(str_buf, 0x0, sizeof(str_buf)); + for (j = 0; j < ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST ; j++) { + stats_value = le32_to_cpu(htt_stats_buf->num_ppdu_cmpl_per_burst + [i * max_ppdu + j]); + str_buf_len += scnprintf(&str_buf[str_buf_len], + ATH12K_HTT_MAX_STRING_LEN - str_buf_len, + " %u:%u,", j, stats_value); + } + /* To overwrite the last trailing comma */ + str_buf[str_buf_len - 1] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_ppdu_completed_per_burst_nr%u = %s\n", + mode, ((i + 1) * 4), str_buf); + str_buf_len = 0; + memset(str_buf, 0x0, sizeof(str_buf)); + for (j = 0; j < ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS ; j++) { + stats_value = le32_to_cpu(htt_stats_buf->num_seq_term_status + [i * max_sched + j]); + str_buf_len += scnprintf(&str_buf[str_buf_len], + ATH12K_HTT_MAX_STRING_LEN - str_buf_len, + " %u:%u,", j, stats_value); + } + /* To overwrite the last trailing comma */ + str_buf[str_buf_len - 1] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_seq_term_status_nr%u = %s\n\n", + mode, ((i + 1) * 4), str_buf); + } + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_sifs_hist_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_sifs_hist_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV:\n"); + + len += print_array_to_buf(buf, len, "sifs_hist_status", + htt_stats_buf->sifs_hist_status, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_pdev_ctrl_path_tx_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_ctrl_path_tx_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_CTRL_PATH_TX_STATS:\n"); + len += print_array_to_buf(buf, len, "fw_tx_mgmt_subtype", + htt_stats_buf->fw_tx_mgmt_subtype, + ATH12K_HTT_STATS_SUBTYPE_MAX, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_stats_tx_sched_cmn_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_stats_tx_sched_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "current_timestamp = %u\n\n", + le32_to_cpu(htt_stats_buf->current_timestamp)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_pdev_stats_sched_per_txq_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_sched_per_txq_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, + ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "txq_id = %u\n", + u32_get_bits(mac_id_word, + ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID)); + len += scnprintf(buf + len, buf_len - len, "sched_policy = %u\n", + le32_to_cpu(htt_stats_buf->sched_policy)); + len += scnprintf(buf + len, buf_len - len, + "last_sched_cmd_posted_timestamp = %u\n", + le32_to_cpu(htt_stats_buf->last_sched_cmd_posted_timestamp)); + len += scnprintf(buf + len, buf_len - len, + "last_sched_cmd_compl_timestamp = %u\n", + le32_to_cpu(htt_stats_buf->last_sched_cmd_compl_timestamp)); + len += scnprintf(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u\n", + le32_to_cpu(htt_stats_buf->sched_2_tac_lwm_count)); + len += scnprintf(buf + len, buf_len - len, "sched_2_tac_ring_full = %u\n", + le32_to_cpu(htt_stats_buf->sched_2_tac_ring_full)); + len += scnprintf(buf + len, buf_len - len, "sched_cmd_post_failure = %u\n", + le32_to_cpu(htt_stats_buf->sched_cmd_post_failure)); + len += scnprintf(buf + len, buf_len - len, "num_active_tids = %u\n", + le32_to_cpu(htt_stats_buf->num_active_tids)); + len += scnprintf(buf + len, buf_len - len, "num_ps_schedules = %u\n", + le32_to_cpu(htt_stats_buf->num_ps_schedules)); + len += scnprintf(buf + len, buf_len - len, "sched_cmds_pending = %u\n", + le32_to_cpu(htt_stats_buf->sched_cmds_pending)); + len += scnprintf(buf + len, buf_len - len, "num_tid_register = %u\n", + le32_to_cpu(htt_stats_buf->num_tid_register)); + len += scnprintf(buf + len, buf_len - len, "num_tid_unregister = %u\n", + le32_to_cpu(htt_stats_buf->num_tid_unregister)); + len += scnprintf(buf + len, buf_len - len, "num_qstats_queried = %u\n", + le32_to_cpu(htt_stats_buf->num_qstats_queried)); + len += scnprintf(buf + len, buf_len - len, "qstats_update_pending = %u\n", + le32_to_cpu(htt_stats_buf->qstats_update_pending)); + len += scnprintf(buf + len, buf_len - len, "last_qstats_query_timestamp = %u\n", + le32_to_cpu(htt_stats_buf->last_qstats_query_timestamp)); + len += scnprintf(buf + len, buf_len - len, "num_tqm_cmdq_full = %u\n", + le32_to_cpu(htt_stats_buf->num_tqm_cmdq_full)); + len += scnprintf(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u\n", + le32_to_cpu(htt_stats_buf->num_de_sched_algo_trigger)); + len += scnprintf(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u\n", + le32_to_cpu(htt_stats_buf->num_rt_sched_algo_trigger)); + len += scnprintf(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u\n", + le32_to_cpu(htt_stats_buf->num_tqm_sched_algo_trigger)); + len += scnprintf(buf + len, buf_len - len, "notify_sched = %u\n", + le32_to_cpu(htt_stats_buf->notify_sched)); + len += scnprintf(buf + len, buf_len - len, "dur_based_sendn_term = %u\n", + le32_to_cpu(htt_stats_buf->dur_based_sendn_term)); + len += scnprintf(buf + len, buf_len - len, "su_notify2_sched = %u\n", + le32_to_cpu(htt_stats_buf->su_notify2_sched)); + len += scnprintf(buf + len, buf_len - len, "su_optimal_queued_msdus_sched = %u\n", + le32_to_cpu(htt_stats_buf->su_optimal_queued_msdus_sched)); + len += scnprintf(buf + len, buf_len - len, "su_delay_timeout_sched = %u\n", + le32_to_cpu(htt_stats_buf->su_delay_timeout_sched)); + len += scnprintf(buf + len, buf_len - len, "su_min_txtime_sched_delay = %u\n", + le32_to_cpu(htt_stats_buf->su_min_txtime_sched_delay)); + len += scnprintf(buf + len, buf_len - len, "su_no_delay = %u\n", + le32_to_cpu(htt_stats_buf->su_no_delay)); + len += scnprintf(buf + len, buf_len - len, "num_supercycles = %u\n", + le32_to_cpu(htt_stats_buf->num_supercycles)); + len += scnprintf(buf + len, buf_len - len, "num_subcycles_with_sort = %u\n", + le32_to_cpu(htt_stats_buf->num_subcycles_with_sort)); + len += scnprintf(buf + len, buf_len - len, "num_subcycles_no_sort = %u\n\n", + le32_to_cpu(htt_stats_buf->num_subcycles_no_sort)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_cmd_posted_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_cmd_posted_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elements = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV:\n"); + len += print_array_to_buf(buf, len, "sched_cmd_posted", + htt_stats_buf->sched_cmd_posted, num_elements, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_cmd_reaped_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_cmd_reaped_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elements = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV:\n"); + len += print_array_to_buf(buf, len, "sched_cmd_reaped", + htt_stats_buf->sched_cmd_reaped, num_elements, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_sched_order_su_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_sched_order_su_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 sched_order_su_num_entries = min_t(u32, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV:\n"); + len += print_array_to_buf(buf, len, "sched_order_su", + htt_stats_buf->sched_order_su, + sched_order_su_num_entries, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_sched_ineligibility_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_sched_ineligibility_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 sched_ineligibility_num_entries = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY:\n"); + len += print_array_to_buf(buf, len, "sched_ineligibility", + htt_stats_buf->sched_ineligibility, + sched_ineligibility_num_entries, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_supercycle_trigger_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_supercycle_triggers_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_MAX); + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SUPERCYCLE_TRIGGER:\n"); + len += print_array_to_buf(buf, len, "supercycle_triggers", + htt_stats_buf->supercycle_triggers, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_stats_pdev_errs_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_stats_pdev_errs_tlv *htt_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_buf)) + return; + + mac_id_word = le32_to_cpu(htt_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", + le32_to_cpu(htt_buf->tx_abort)); + len += scnprintf(buf + len, buf_len - len, "tx_abort_fail_count = %u\n", + le32_to_cpu(htt_buf->tx_abort_fail_count)); + len += scnprintf(buf + len, buf_len - len, "rx_abort = %u\n", + le32_to_cpu(htt_buf->rx_abort)); + len += scnprintf(buf + len, buf_len - len, "rx_abort_fail_count = %u\n", + le32_to_cpu(htt_buf->rx_abort_fail_count)); + len += scnprintf(buf + len, buf_len - len, "rx_flush_cnt = %u\n", + le32_to_cpu(htt_buf->rx_flush_cnt)); + len += scnprintf(buf + len, buf_len - len, "warm_reset = %u\n", + le32_to_cpu(htt_buf->warm_reset)); + len += scnprintf(buf + len, buf_len - len, "cold_reset = %u\n", + le32_to_cpu(htt_buf->cold_reset)); + len += scnprintf(buf + len, buf_len - len, "mac_cold_reset_restore_cal = %u\n", + le32_to_cpu(htt_buf->mac_cold_reset_restore_cal)); + len += scnprintf(buf + len, buf_len - len, "mac_cold_reset = %u\n", + le32_to_cpu(htt_buf->mac_cold_reset)); + len += scnprintf(buf + len, buf_len - len, "mac_warm_reset = %u\n", + le32_to_cpu(htt_buf->mac_warm_reset)); + len += scnprintf(buf + len, buf_len - len, "mac_only_reset = %u\n", + le32_to_cpu(htt_buf->mac_only_reset)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_ucode_trig = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_ucode_trig)); + len += scnprintf(buf + len, buf_len - len, "mac_warm_reset_restore_cal = %u\n", + le32_to_cpu(htt_buf->mac_warm_reset_restore_cal)); + len += scnprintf(buf + len, buf_len - len, "mac_sfm_reset = %u\n", + le32_to_cpu(htt_buf->mac_sfm_reset)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_m3_ssr = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_m3_ssr)); + len += scnprintf(buf + len, buf_len - len, "fw_rx_rings_reset = %u\n", + le32_to_cpu(htt_buf->fw_rx_rings_reset)); + len += scnprintf(buf + len, buf_len - len, "tx_flush = %u\n", + le32_to_cpu(htt_buf->tx_flush)); + len += scnprintf(buf + len, buf_len - len, "tx_glb_reset = %u\n", + le32_to_cpu(htt_buf->tx_glb_reset)); + len += scnprintf(buf + len, buf_len - len, "tx_txq_reset = %u\n", + le32_to_cpu(htt_buf->tx_txq_reset)); + len += scnprintf(buf + len, buf_len - len, "rx_timeout_reset = %u\n\n", + le32_to_cpu(htt_buf->rx_timeout_reset)); + + len += scnprintf(buf + len, buf_len - len, "PDEV_PHY_WARM_RESET_REASONS:\n"); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_reason_phy_m3 = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_phy_m3)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_hw_stuck = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_hw_stuck)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_num_cca_rx_frame_stuck = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_num_rx_frame_stuck)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_wal_rx_recovery_rst_rx_busy = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_wal_rx_rec_rx_busy)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_wal_rx_recovery_rst_mac_hang = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_wal_rx_rec_mac_hng)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_mac_reset_converted_phy_reset = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_mac_conv_phy_reset)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_lifetime_expiry_cca_stuck = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_exp_cca_stuck)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_consecutive_flush9_war = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_consec_flsh_war)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_hwsch_reset_war = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_hwsch_reset_war)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_hwsch_wdog_or_cca_wdog_war = %u\n\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_hwsch_cca_wdog_war)); + + len += scnprintf(buf + len, buf_len - len, "WAL_RX_RECOVERY_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_mac_hang_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_mac_hang_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_known_sig_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_known_sig_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_no_rx_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_no_rx_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_no_rx_consecutive_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_no_rx_consec_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_rx_busy_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_rx_busy_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_phy_mac_hang_count = %u\n\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_phy_mac_hang_cnt)); + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_DEST_DRAIN_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rx_descs_leak_prevention_done = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rx_descs_leak_prevented)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rx_descs_saved_cnt = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rx_descs_saved_cnt)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma2reo_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma2reo_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma2fw_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma2fw_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma2wbm_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma2wbm_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma1_2sw_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma1_2sw_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rx_drain_ok_mac_idle = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rx_drain_ok_mac_idle)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_ok_mac_not_idle = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_ok_mac_not_idle)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_prerequisite_invld = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_prerequisite_invld)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_skip_for_non_lmac_reset = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_skip_non_lmac_reset)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_hw_fifo_not_empty_post_drain_wait = %u\n\n", + le32_to_cpu(htt_buf->rx_dest_drain_hw_fifo_notempty_post_wait)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_stats_intr_misc_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_stats_intr_misc_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "hw_intr_name = %s\n", + htt_stats_buf->hw_intr_name); + len += scnprintf(buf + len, buf_len - len, "mask = %u\n", + le32_to_cpu(htt_stats_buf->mask)); + len += scnprintf(buf + len, buf_len - len, "count = %u\n\n", + le32_to_cpu(htt_stats_buf->count)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_stats_whal_tx_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_stats_whal_tx_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "last_unpause_ppdu_id = %u\n", + le32_to_cpu(htt_stats_buf->last_unpause_ppdu_id)); + len += scnprintf(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_unpause_wait_tqm_write)); + len += scnprintf(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_dummy_tlv_skipped)); + len += scnprintf(buf + len, buf_len - len, + "hwsch_misaligned_offset_received = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_misaligned_offset_received)); + len += scnprintf(buf + len, buf_len - len, "hwsch_reset_count = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_reset_count)); + len += scnprintf(buf + len, buf_len - len, "hwsch_dev_reset_war = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_dev_reset_war)); + len += scnprintf(buf + len, buf_len - len, "hwsch_delayed_pause = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_delayed_pause)); + len += scnprintf(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_long_delayed_pause)); + len += scnprintf(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u\n", + le32_to_cpu(htt_stats_buf->sch_rx_ppdu_no_response)); + len += scnprintf(buf + len, buf_len - len, "sch_selfgen_response = %u\n", + le32_to_cpu(htt_stats_buf->sch_selfgen_response)); + len += scnprintf(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n\n", + le32_to_cpu(htt_stats_buf->sch_rx_sifs_resp_trigger)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_war_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_war_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 fixed_len, array_len; + u8 i, array_words; + u32 mac_id; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + fixed_len = sizeof(*htt_stats_buf); + array_len = tag_len - fixed_len; + array_words = array_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_WAR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + + for (i = 0; i < array_words; i++) { + len += scnprintf(buf + len, buf_len - len, "hw_war %u = %u\n\n", + i, le32_to_cpu(htt_stats_buf->hw_wars[i])); + } + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_cmn_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "max_cmdq_id = %u\n", + le32_to_cpu(htt_stats_buf->max_cmdq_id)); + len += scnprintf(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u\n", + le32_to_cpu(htt_stats_buf->list_mpdu_cnt_hist_intvl)); + len += scnprintf(buf + len, buf_len - len, "add_msdu = %u\n", + le32_to_cpu(htt_stats_buf->add_msdu)); + len += scnprintf(buf + len, buf_len - len, "q_empty = %u\n", + le32_to_cpu(htt_stats_buf->q_empty)); + len += scnprintf(buf + len, buf_len - len, "q_not_empty = %u\n", + le32_to_cpu(htt_stats_buf->q_not_empty)); + len += scnprintf(buf + len, buf_len - len, "drop_notification = %u\n", + le32_to_cpu(htt_stats_buf->drop_notification)); + len += scnprintf(buf + len, buf_len - len, "desc_threshold = %u\n", + le32_to_cpu(htt_stats_buf->desc_threshold)); + len += scnprintf(buf + len, buf_len - len, "hwsch_tqm_invalid_status = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_tqm_invalid_status)); + len += scnprintf(buf + len, buf_len - len, "missed_tqm_gen_mpdus = %u\n", + le32_to_cpu(htt_stats_buf->missed_tqm_gen_mpdus)); + len += scnprintf(buf + len, buf_len - len, + "total_msduq_timestamp_updates = %u\n", + le32_to_cpu(htt_stats_buf->msduq_timestamp_updates)); + len += scnprintf(buf + len, buf_len - len, + "total_msduq_timestamp_updates_by_get_mpdu_head_info_cmd = %u\n", + le32_to_cpu(htt_stats_buf->msduq_updates_mpdu_head_info_cmd)); + len += scnprintf(buf + len, buf_len - len, + "total_msduq_timestamp_updates_by_emp_to_nonemp_status = %u\n", + le32_to_cpu(htt_stats_buf->msduq_updates_emp_to_nonemp_status)); + len += scnprintf(buf + len, buf_len - len, + "total_get_mpdu_head_info_cmds_by_sched_algo_la_query = %u\n", + le32_to_cpu(htt_stats_buf->get_mpdu_head_info_cmds_by_query)); + len += scnprintf(buf + len, buf_len - len, + "total_get_mpdu_head_info_cmds_by_tac = %u\n", + le32_to_cpu(htt_stats_buf->get_mpdu_head_info_cmds_by_tac)); + len += scnprintf(buf + len, buf_len - len, + "total_gen_mpdu_cmds_by_sched_algo_la_query = %u\n", + le32_to_cpu(htt_stats_buf->gen_mpdu_cmds_by_query)); + len += scnprintf(buf + len, buf_len - len, "active_tqm_tids = %u\n", + le32_to_cpu(htt_stats_buf->tqm_active_tids)); + len += scnprintf(buf + len, buf_len - len, "inactive_tqm_tids = %u\n", + le32_to_cpu(htt_stats_buf->tqm_inactive_tids)); + len += scnprintf(buf + len, buf_len - len, "tqm_active_msduq_flows = %u\n", + le32_to_cpu(htt_stats_buf->tqm_active_msduq_flows)); + len += scnprintf(buf + len, buf_len - len, "hi_prio_q_not_empty = %u\n\n", + le32_to_cpu(htt_stats_buf->high_prio_q_not_empty)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_error_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_error_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_ERROR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "q_empty_failure = %u\n", + le32_to_cpu(htt_stats_buf->q_empty_failure)); + len += scnprintf(buf + len, buf_len - len, "q_not_empty_failure = %u\n", + le32_to_cpu(htt_stats_buf->q_not_empty_failure)); + len += scnprintf(buf + len, buf_len - len, "add_msdu_failure = %u\n\n", + le32_to_cpu(htt_stats_buf->add_msdu_failure)); + + len += scnprintf(buf + len, buf_len - len, "TQM_ERROR_RESET_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "tqm_cache_ctl_err = %u\n", + le32_to_cpu(htt_stats_buf->tqm_cache_ctl_err)); + len += scnprintf(buf + len, buf_len - len, "tqm_soft_reset = %u\n", + le32_to_cpu(htt_stats_buf->tqm_soft_reset)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_total_num_in_use_link_descs = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_in_use_link_descs)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_worst_case_num_lost_link_descs = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_lost_link_descs)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_worst_case_num_lost_host_tx_bufs_count = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_lost_host_tx_buf_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_num_in_use_link_descs_internal_tqm = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_in_use_internal_tqm)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_num_in_use_link_descs_wbm_idle_link_ring = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_in_use_idle_link_rng)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_time_to_tqm_hang_delta_ms = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_time_to_tqm_hang_delta_ms)); + len += scnprintf(buf + len, buf_len - len, "tqm_reset_recovery_time_ms = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_recovery_time_ms)); + len += scnprintf(buf + len, buf_len - len, "tqm_reset_num_peers_hdl = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_peers_hdl)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_cumm_dirty_hw_mpduq_proc_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_cumm_dirty_hw_mpduq_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_cumm_dirty_hw_msduq_proc = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_cumm_dirty_hw_msduq_proc)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_su_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_su_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_other_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_other_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_trig_type = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_trig_type)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_trig_cfg = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_trig_cfg)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_skip_cmd_status_null = %u\n\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cmd_skp_status_null)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_gen_mpdu_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_gen_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elements = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "gen_mpdu_end_reason", + htt_stats_buf->gen_mpdu_end_reason, num_elements, + "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_list_mpdu_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_list_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "list_mpdu_end_reason", + htt_stats_buf->list_mpdu_end_reason, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_list_mpdu_cnt_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_list_mpdu_cnt_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:\n"); + len += print_array_to_buf(buf, len, "list_mpdu_cnt_hist", + htt_stats_buf->list_mpdu_cnt_hist, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_pdev_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_pdev_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_PDEV_STATS_TLV_V:\n"); + len += scnprintf(buf + len, buf_len - len, "msdu_count = %u\n", + le32_to_cpu(htt_stats_buf->msdu_count)); + len += scnprintf(buf + len, buf_len - len, "mpdu_count = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_count)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdu = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdu)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu_ttl = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu_ttl)); + len += scnprintf(buf + len, buf_len - len, "send_bar = %u\n", + le32_to_cpu(htt_stats_buf->send_bar)); + len += scnprintf(buf + len, buf_len - len, "bar_sync = %u\n", + le32_to_cpu(htt_stats_buf->bar_sync)); + len += scnprintf(buf + len, buf_len - len, "notify_mpdu = %u\n", + le32_to_cpu(htt_stats_buf->notify_mpdu)); + len += scnprintf(buf + len, buf_len - len, "sync_cmd = %u\n", + le32_to_cpu(htt_stats_buf->sync_cmd)); + len += scnprintf(buf + len, buf_len - len, "write_cmd = %u\n", + le32_to_cpu(htt_stats_buf->write_cmd)); + len += scnprintf(buf + len, buf_len - len, "hwsch_trigger = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_trigger)); + len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", + le32_to_cpu(htt_stats_buf->ack_tlv_proc)); + len += scnprintf(buf + len, buf_len - len, "gen_mpdu_cmd = %u\n", + le32_to_cpu(htt_stats_buf->gen_mpdu_cmd)); + len += scnprintf(buf + len, buf_len - len, "gen_list_cmd = %u\n", + le32_to_cpu(htt_stats_buf->gen_list_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdu_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdu_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdu_tried_cmd)); + len += scnprintf(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_queue_stats_cmd)); + len += scnprintf(buf + len, buf_len - len, "mpdu_head_info_cmd = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_head_info_cmd)); + len += scnprintf(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u\n", + le32_to_cpu(htt_stats_buf->msdu_flow_stats_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu_ttl_cmd)); + len += scnprintf(buf + len, buf_len - len, "flush_cache_cmd = %u\n", + le32_to_cpu(htt_stats_buf->flush_cache_cmd)); + len += scnprintf(buf + len, buf_len - len, "update_mpduq_cmd = %u\n", + le32_to_cpu(htt_stats_buf->update_mpduq_cmd)); + len += scnprintf(buf + len, buf_len - len, "enqueue = %u\n", + le32_to_cpu(htt_stats_buf->enqueue)); + len += scnprintf(buf + len, buf_len - len, "enqueue_notify = %u\n", + le32_to_cpu(htt_stats_buf->enqueue_notify)); + len += scnprintf(buf + len, buf_len - len, "notify_mpdu_at_head = %u\n", + le32_to_cpu(htt_stats_buf->notify_mpdu_at_head)); + len += scnprintf(buf + len, buf_len - len, "notify_mpdu_state_valid = %u\n", + le32_to_cpu(htt_stats_buf->notify_mpdu_state_valid)); + len += scnprintf(buf + len, buf_len - len, "sched_udp_notify1 = %u\n", + le32_to_cpu(htt_stats_buf->sched_udp_notify1)); + len += scnprintf(buf + len, buf_len - len, "sched_udp_notify2 = %u\n", + le32_to_cpu(htt_stats_buf->sched_udp_notify2)); + len += scnprintf(buf + len, buf_len - len, "sched_nonudp_notify1 = %u\n", + le32_to_cpu(htt_stats_buf->sched_nonudp_notify1)); + len += scnprintf(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n\n", + le32_to_cpu(htt_stats_buf->sched_nonudp_notify2)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_cmn_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_CMN_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "tcl2fw_entry_count = %u\n", + le32_to_cpu(htt_stats_buf->tcl2fw_entry_count)); + len += scnprintf(buf + len, buf_len - len, "not_to_fw = %u\n", + le32_to_cpu(htt_stats_buf->not_to_fw)); + len += scnprintf(buf + len, buf_len - len, "invalid_pdev_vdev_peer = %u\n", + le32_to_cpu(htt_stats_buf->invalid_pdev_vdev_peer)); + len += scnprintf(buf + len, buf_len - len, "tcl_res_invalid_addrx = %u\n", + le32_to_cpu(htt_stats_buf->tcl_res_invalid_addrx)); + len += scnprintf(buf + len, buf_len - len, "wbm2fw_entry_count = %u\n", + le32_to_cpu(htt_stats_buf->wbm2fw_entry_count)); + len += scnprintf(buf + len, buf_len - len, "invalid_pdev = %u\n", + le32_to_cpu(htt_stats_buf->invalid_pdev)); + len += scnprintf(buf + len, buf_len - len, "tcl_res_addrx_timeout = %u\n", + le32_to_cpu(htt_stats_buf->tcl_res_addrx_timeout)); + len += scnprintf(buf + len, buf_len - len, "invalid_vdev = %u\n", + le32_to_cpu(htt_stats_buf->invalid_vdev)); + len += scnprintf(buf + len, buf_len - len, "invalid_tcl_exp_frame_desc = %u\n", + le32_to_cpu(htt_stats_buf->invalid_tcl_exp_frame_desc)); + len += scnprintf(buf + len, buf_len - len, "vdev_id_mismatch_count = %u\n\n", + le32_to_cpu(htt_stats_buf->vdev_id_mismatch_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_eapol_packets_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_eapol_packets_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_DE_EAPOL_PACKETS_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "m1_packets = %u\n", + le32_to_cpu(htt_stats_buf->m1_packets)); + len += scnprintf(buf + len, buf_len - len, "m2_packets = %u\n", + le32_to_cpu(htt_stats_buf->m2_packets)); + len += scnprintf(buf + len, buf_len - len, "m3_packets = %u\n", + le32_to_cpu(htt_stats_buf->m3_packets)); + len += scnprintf(buf + len, buf_len - len, "m4_packets = %u\n", + le32_to_cpu(htt_stats_buf->m4_packets)); + len += scnprintf(buf + len, buf_len - len, "g1_packets = %u\n", + le32_to_cpu(htt_stats_buf->g1_packets)); + len += scnprintf(buf + len, buf_len - len, "g2_packets = %u\n", + le32_to_cpu(htt_stats_buf->g2_packets)); + len += scnprintf(buf + len, buf_len - len, "rc4_packets = %u\n", + le32_to_cpu(htt_stats_buf->rc4_packets)); + len += scnprintf(buf + len, buf_len - len, "eap_packets = %u\n", + le32_to_cpu(htt_stats_buf->eap_packets)); + len += scnprintf(buf + len, buf_len - len, "eapol_start_packets = %u\n", + le32_to_cpu(htt_stats_buf->eapol_start_packets)); + len += scnprintf(buf + len, buf_len - len, "eapol_logoff_packets = %u\n", + le32_to_cpu(htt_stats_buf->eapol_logoff_packets)); + len += scnprintf(buf + len, buf_len - len, "eapol_encap_asf_packets = %u\n\n", + le32_to_cpu(htt_stats_buf->eapol_encap_asf_packets)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_classify_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_classify_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_CLASSIFY_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "arp_packets = %u\n", + le32_to_cpu(htt_stats_buf->arp_packets)); + len += scnprintf(buf + len, buf_len - len, "igmp_packets = %u\n", + le32_to_cpu(htt_stats_buf->igmp_packets)); + len += scnprintf(buf + len, buf_len - len, "dhcp_packets = %u\n", + le32_to_cpu(htt_stats_buf->dhcp_packets)); + len += scnprintf(buf + len, buf_len - len, "host_inspected = %u\n", + le32_to_cpu(htt_stats_buf->host_inspected)); + len += scnprintf(buf + len, buf_len - len, "htt_included = %u\n", + le32_to_cpu(htt_stats_buf->htt_included)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_mcs = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_mcs)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_nss = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_nss)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_preamble_type = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_preamble_type)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_chainmask = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_chainmask)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_guard_interval = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_guard_interval)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_retries = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_retries)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_bw_info = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_bw_info)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_power = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_power)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_key_flags = 0x%x\n", + le32_to_cpu(htt_stats_buf->htt_valid_key_flags)); + len += scnprintf(buf + len, buf_len - len, "htt_valid_no_encryption = %u\n", + le32_to_cpu(htt_stats_buf->htt_valid_no_encryption)); + len += scnprintf(buf + len, buf_len - len, "fse_entry_count = %u\n", + le32_to_cpu(htt_stats_buf->fse_entry_count)); + len += scnprintf(buf + len, buf_len - len, "fse_priority_be = %u\n", + le32_to_cpu(htt_stats_buf->fse_priority_be)); + len += scnprintf(buf + len, buf_len - len, "fse_priority_high = %u\n", + le32_to_cpu(htt_stats_buf->fse_priority_high)); + len += scnprintf(buf + len, buf_len - len, "fse_priority_low = %u\n", + le32_to_cpu(htt_stats_buf->fse_priority_low)); + len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_be = %u\n", + le32_to_cpu(htt_stats_buf->fse_traffic_ptrn_be)); + len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_over_sub = %u\n", + le32_to_cpu(htt_stats_buf->fse_traffic_ptrn_over_sub)); + len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_bursty = %u\n", + le32_to_cpu(htt_stats_buf->fse_traffic_ptrn_bursty)); + len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_interactive = %u\n", + le32_to_cpu(htt_stats_buf->fse_traffic_ptrn_interactive)); + len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_periodic = %u\n", + le32_to_cpu(htt_stats_buf->fse_traffic_ptrn_periodic)); + len += scnprintf(buf + len, buf_len - len, "fse_hwqueue_alloc = %u\n", + le32_to_cpu(htt_stats_buf->fse_hwqueue_alloc)); + len += scnprintf(buf + len, buf_len - len, "fse_hwqueue_created = %u\n", + le32_to_cpu(htt_stats_buf->fse_hwqueue_created)); + len += scnprintf(buf + len, buf_len - len, "fse_hwqueue_send_to_host = %u\n", + le32_to_cpu(htt_stats_buf->fse_hwqueue_send_to_host)); + len += scnprintf(buf + len, buf_len - len, "mcast_entry = %u\n", + le32_to_cpu(htt_stats_buf->mcast_entry)); + len += scnprintf(buf + len, buf_len - len, "bcast_entry = %u\n", + le32_to_cpu(htt_stats_buf->bcast_entry)); + len += scnprintf(buf + len, buf_len - len, "htt_update_peer_cache = %u\n", + le32_to_cpu(htt_stats_buf->htt_update_peer_cache)); + len += scnprintf(buf + len, buf_len - len, "htt_learning_frame = %u\n", + le32_to_cpu(htt_stats_buf->htt_learning_frame)); + len += scnprintf(buf + len, buf_len - len, "fse_invalid_peer = %u\n", + le32_to_cpu(htt_stats_buf->fse_invalid_peer)); + len += scnprintf(buf + len, buf_len - len, "mec_notify = %u\n\n", + le32_to_cpu(htt_stats_buf->mec_notify)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_classify_failed_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_classify_failed_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_DE_CLASSIFY_FAILED_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ap_bss_peer_not_found = %u\n", + le32_to_cpu(htt_stats_buf->ap_bss_peer_not_found)); + len += scnprintf(buf + len, buf_len - len, "ap_bcast_mcast_no_peer = %u\n", + le32_to_cpu(htt_stats_buf->ap_bcast_mcast_no_peer)); + len += scnprintf(buf + len, buf_len - len, "sta_delete_in_progress = %u\n", + le32_to_cpu(htt_stats_buf->sta_delete_in_progress)); + len += scnprintf(buf + len, buf_len - len, "ibss_no_bss_peer = %u\n", + le32_to_cpu(htt_stats_buf->ibss_no_bss_peer)); + len += scnprintf(buf + len, buf_len - len, "invalid_vdev_type = %u\n", + le32_to_cpu(htt_stats_buf->invalid_vdev_type)); + len += scnprintf(buf + len, buf_len - len, "invalid_ast_peer_entry = %u\n", + le32_to_cpu(htt_stats_buf->invalid_ast_peer_entry)); + len += scnprintf(buf + len, buf_len - len, "peer_entry_invalid = %u\n", + le32_to_cpu(htt_stats_buf->peer_entry_invalid)); + len += scnprintf(buf + len, buf_len - len, "ethertype_not_ip = %u\n", + le32_to_cpu(htt_stats_buf->ethertype_not_ip)); + len += scnprintf(buf + len, buf_len - len, "eapol_lookup_failed = %u\n", + le32_to_cpu(htt_stats_buf->eapol_lookup_failed)); + len += scnprintf(buf + len, buf_len - len, "qpeer_not_allow_data = %u\n", + le32_to_cpu(htt_stats_buf->qpeer_not_allow_data)); + len += scnprintf(buf + len, buf_len - len, "fse_tid_override = %u\n", + le32_to_cpu(htt_stats_buf->fse_tid_override)); + len += scnprintf(buf + len, buf_len - len, "ipv6_jumbogram_zero_length = %u\n", + le32_to_cpu(htt_stats_buf->ipv6_jumbogram_zero_length)); + len += scnprintf(buf + len, buf_len - len, "qos_to_non_qos_in_prog = %u\n", + le32_to_cpu(htt_stats_buf->qos_to_non_qos_in_prog)); + len += scnprintf(buf + len, buf_len - len, "ap_bcast_mcast_eapol = %u\n", + le32_to_cpu(htt_stats_buf->ap_bcast_mcast_eapol)); + len += scnprintf(buf + len, buf_len - len, "unicast_on_ap_bss_peer = %u\n", + le32_to_cpu(htt_stats_buf->unicast_on_ap_bss_peer)); + len += scnprintf(buf + len, buf_len - len, "ap_vdev_invalid = %u\n", + le32_to_cpu(htt_stats_buf->ap_vdev_invalid)); + len += scnprintf(buf + len, buf_len - len, "incomplete_llc = %u\n", + le32_to_cpu(htt_stats_buf->incomplete_llc)); + len += scnprintf(buf + len, buf_len - len, "eapol_duplicate_m3 = %u\n", + le32_to_cpu(htt_stats_buf->eapol_duplicate_m3)); + len += scnprintf(buf + len, buf_len - len, "eapol_duplicate_m4 = %u\n\n", + le32_to_cpu(htt_stats_buf->eapol_duplicate_m4)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_classify_status_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_classify_status_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_DE_CLASSIFY_STATUS_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "eok = %u\n", + le32_to_cpu(htt_stats_buf->eok)); + len += scnprintf(buf + len, buf_len - len, "classify_done = %u\n", + le32_to_cpu(htt_stats_buf->classify_done)); + len += scnprintf(buf + len, buf_len - len, "lookup_failed = %u\n", + le32_to_cpu(htt_stats_buf->lookup_failed)); + len += scnprintf(buf + len, buf_len - len, "send_host_dhcp = %u\n", + le32_to_cpu(htt_stats_buf->send_host_dhcp)); + len += scnprintf(buf + len, buf_len - len, "send_host_mcast = %u\n", + le32_to_cpu(htt_stats_buf->send_host_mcast)); + len += scnprintf(buf + len, buf_len - len, "send_host_unknown_dest = %u\n", + le32_to_cpu(htt_stats_buf->send_host_unknown_dest)); + len += scnprintf(buf + len, buf_len - len, "send_host = %u\n", + le32_to_cpu(htt_stats_buf->send_host)); + len += scnprintf(buf + len, buf_len - len, "status_invalid = %u\n\n", + le32_to_cpu(htt_stats_buf->status_invalid)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_enqueue_packets_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_enqueue_packets_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_DE_ENQUEUE_PACKETS_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "enqueued_pkts = %u\n", + le32_to_cpu(htt_stats_buf->enqueued_pkts)); + len += scnprintf(buf + len, buf_len - len, "to_tqm = %u\n", + le32_to_cpu(htt_stats_buf->to_tqm)); + len += scnprintf(buf + len, buf_len - len, "to_tqm_bypass = %u\n\n", + le32_to_cpu(htt_stats_buf->to_tqm_bypass)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_enqueue_discard_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_enqueue_discard_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_DE_ENQUEUE_DISCARD_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "discarded_pkts = %u\n", + le32_to_cpu(htt_stats_buf->discarded_pkts)); + len += scnprintf(buf + len, buf_len - len, "local_frames = %u\n", + le32_to_cpu(htt_stats_buf->local_frames)); + len += scnprintf(buf + len, buf_len - len, "is_ext_msdu = %u\n\n", + le32_to_cpu(htt_stats_buf->is_ext_msdu)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_de_compl_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_de_compl_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_COMPL_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "tcl_dummy_frame = %u\n", + le32_to_cpu(htt_stats_buf->tcl_dummy_frame)); + len += scnprintf(buf + len, buf_len - len, "tqm_dummy_frame = %u\n", + le32_to_cpu(htt_stats_buf->tqm_dummy_frame)); + len += scnprintf(buf + len, buf_len - len, "tqm_notify_frame = %u\n", + le32_to_cpu(htt_stats_buf->tqm_notify_frame)); + len += scnprintf(buf + len, buf_len - len, "fw2wbm_enq = %u\n", + le32_to_cpu(htt_stats_buf->fw2wbm_enq)); + len += scnprintf(buf + len, buf_len - len, "tqm_bypass_frame = %u\n\n", + le32_to_cpu(htt_stats_buf->tqm_bypass_frame)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_cmn_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_CMN_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "su_bar = %u\n", + le32_to_cpu(htt_stats_buf->su_bar)); + len += scnprintf(buf + len, buf_len - len, "rts = %u\n", + le32_to_cpu(htt_stats_buf->rts)); + len += scnprintf(buf + len, buf_len - len, "cts2self = %u\n", + le32_to_cpu(htt_stats_buf->cts2self)); + len += scnprintf(buf + len, buf_len - len, "qos_null = %u\n", + le32_to_cpu(htt_stats_buf->qos_null)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_1 = %u\n", + le32_to_cpu(htt_stats_buf->delayed_bar_1)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_2 = %u\n", + le32_to_cpu(htt_stats_buf->delayed_bar_2)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_3 = %u\n", + le32_to_cpu(htt_stats_buf->delayed_bar_3)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_4 = %u\n", + le32_to_cpu(htt_stats_buf->delayed_bar_4)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_5 = %u\n", + le32_to_cpu(htt_stats_buf->delayed_bar_5)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_6 = %u\n", + le32_to_cpu(htt_stats_buf->delayed_bar_6)); + len += scnprintf(buf + len, buf_len - len, "delayed_bar_7 = %u\n\n", + le32_to_cpu(htt_stats_buf->delayed_bar_7)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_ac_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_ac_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ac_su_ndpa_tried = %u\n", + le32_to_cpu(htt_stats_buf->ac_su_ndpa)); + len += scnprintf(buf + len, buf_len - len, "ac_su_ndp_tried = %u\n", + le32_to_cpu(htt_stats_buf->ac_su_ndp)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndpa_tried = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_ndpa)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndp_tried = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_ndp)); + len += print_array_to_buf_index(buf, len, "ac_mu_mimo_brpollX_tried = ", 1, + htt_stats_buf->ac_mu_mimo_brpoll, + ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS - 1, + "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_ax_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_ax_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ax_su_ndpa_tried = %u\n", + le32_to_cpu(htt_stats_buf->ax_su_ndpa)); + len += scnprintf(buf + len, buf_len - len, "ax_su_ndp_tried = %u\n", + le32_to_cpu(htt_stats_buf->ax_su_ndp)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndpa_tried = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_ndpa)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndp_tried = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_ndp)); + len += print_array_to_buf_index(buf, len, "ax_mu_mimo_brpollX_tried = ", 1, + htt_stats_buf->ax_mu_mimo_brpoll, + ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS - 1, "\n"); + len += scnprintf(buf + len, buf_len - len, "ax_basic_trigger = %u\n", + le32_to_cpu(htt_stats_buf->ax_basic_trigger)); + len += scnprintf(buf + len, buf_len - len, "ax_ulmumimo_total_trigger = %u\n", + le32_to_cpu(htt_stats_buf->ax_ulmumimo_trigger)); + len += scnprintf(buf + len, buf_len - len, "ax_bsr_trigger = %u\n", + le32_to_cpu(htt_stats_buf->ax_bsr_trigger)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_bar_trigger = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_bar_trigger)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_rts_trigger = %u\n\n", + le32_to_cpu(htt_stats_buf->ax_mu_rts_trigger)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_be_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_be_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_BE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "be_su_ndpa_queued = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndpa_queued)); + len += scnprintf(buf + len, buf_len - len, "be_su_ndpa_tried = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndpa)); + len += scnprintf(buf + len, buf_len - len, "be_su_ndp_queued = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndp_queued)); + len += scnprintf(buf + len, buf_len - len, "be_su_ndp_tried = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndp)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndpa_queued = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndpa_queued)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndpa_tried = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndpa)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndp_queued = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndp_queued)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndp_tried = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndp)); + len += print_array_to_buf_index(buf, len, "be_mu_mimo_brpollX_queued = ", 1, + htt_stats_buf->be_mu_mimo_brpoll_queued, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1, + "\n"); + len += print_array_to_buf_index(buf, len, "be_mu_mimo_brpollX_tried = ", 1, + htt_stats_buf->be_mu_mimo_brpoll, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1, + "\n"); + len += print_array_to_buf(buf, len, "be_ul_mumimo_trigger = ", + htt_stats_buf->be_ul_mumimo_trigger, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS, "\n"); + len += scnprintf(buf + len, buf_len - len, "be_basic_trigger = %u\n", + le32_to_cpu(htt_stats_buf->be_basic_trigger)); + len += scnprintf(buf + len, buf_len - len, "be_ulmumimo_total_trigger = %u\n", + le32_to_cpu(htt_stats_buf->be_ulmumimo_trigger)); + len += scnprintf(buf + len, buf_len - len, "be_bsr_trigger = %u\n", + le32_to_cpu(htt_stats_buf->be_bsr_trigger)); + len += scnprintf(buf + len, buf_len - len, "be_mu_bar_trigger = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_bar_trigger)); + len += scnprintf(buf + len, buf_len - len, "be_mu_rts_trigger = %u\n\n", + le32_to_cpu(htt_stats_buf->be_mu_rts_trigger)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_ac_err_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_ac_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_ERR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ac_su_ndp_err = %u\n", + le32_to_cpu(htt_stats_buf->ac_su_ndp_err)); + len += scnprintf(buf + len, buf_len - len, "ac_su_ndpa_err = %u\n", + le32_to_cpu(htt_stats_buf->ac_su_ndpa_err)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndpa_err = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_ndpa_err)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndp_err = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_ndp_err)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brp1_err = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_brp1_err)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brp2_err = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_brp2_err)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brp3_err = %u\n\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_brp3_err)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_ax_err_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_ax_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_ERR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ax_su_ndp_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_su_ndp_err)); + len += scnprintf(buf + len, buf_len - len, "ax_su_ndpa_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_su_ndpa_err)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndpa_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_ndpa_err)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndp_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_ndp_err)); + len += print_array_to_buf_index(buf, len, "ax_mu_mimo_brpX_err", 1, + htt_stats_buf->ax_mu_mimo_brp_err, + ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS - 1, + "\n"); + len += scnprintf(buf + len, buf_len - len, "ax_basic_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_basic_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "ax_ulmumimo_total_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_ulmumimo_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "ax_bsr_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_bsr_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_bar_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_rts_trigger_err = %u\n\n", + le32_to_cpu(htt_stats_buf->ax_mu_rts_trigger_err)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_be_err_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_selfgen_be_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_BE_ERR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "be_su_ndp_err = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndp_err)); + len += scnprintf(buf + len, buf_len - len, "be_su_ndp_flushed = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndp_flushed)); + len += scnprintf(buf + len, buf_len - len, "be_su_ndpa_err = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndpa_err)); + len += scnprintf(buf + len, buf_len - len, "be_su_ndpa_flushed = %u\n", + le32_to_cpu(htt_stats_buf->be_su_ndpa_flushed)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndpa_err = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndpa_err)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndpa_flushed = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndpa_flushed)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndp_err = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndp_err)); + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_ndp_flushed = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_mimo_ndp_flushed)); + len += print_array_to_buf_index(buf, len, "be_mu_mimo_brpX_err", 1, + htt_stats_buf->be_mu_mimo_brp_err, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1, + "\n"); + len += print_array_to_buf_index(buf, len, "be_mu_mimo_brpollX_flushed", 1, + htt_stats_buf->be_mu_mimo_brpoll_flushed, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1, + "\n"); + len += print_array_to_buf(buf, len, "be_mu_mimo_num_cbf_rcvd_on_brp_err", + htt_stats_buf->be_mu_mimo_brp_err_num_cbf_rxd, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS, "\n"); + len += print_array_to_buf(buf, len, "be_ul_mumimo_trigger_err", + htt_stats_buf->be_ul_mumimo_trigger_err, + ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS, "\n"); + len += scnprintf(buf + len, buf_len - len, "be_basic_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->be_basic_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "be_ulmumimo_total_trig_err = %u\n", + le32_to_cpu(htt_stats_buf->be_ulmumimo_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "be_bsr_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->be_bsr_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "be_mu_bar_trigger_err = %u\n", + le32_to_cpu(htt_stats_buf->be_mu_bar_trigger_err)); + len += scnprintf(buf + len, buf_len - len, "be_mu_rts_trigger_err = %u\n\n", + le32_to_cpu(htt_stats_buf->be_mu_rts_trigger_err)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_ac_sched_status_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats) +{ + const struct ath12k_htt_tx_selfgen_ac_sched_status_stats_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats->buf; + u32 len = stats->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_SELFGEN_AC_SCHED_STATUS_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "ac_su_ndpa_sch_status", + htt_stats_buf->ac_su_ndpa_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ac_su_ndp_sch_status", + htt_stats_buf->ac_su_ndp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_ndpa_sch_status", + htt_stats_buf->ac_mu_mimo_ndpa_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_ndp_sch_status", + htt_stats_buf->ac_mu_mimo_ndp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_brp_sch_status", + htt_stats_buf->ac_mu_mimo_brp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ac_su_ndp_sch_flag_err", + htt_stats_buf->ac_su_ndp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_ndp_sch_flag_err", + htt_stats_buf->ac_mu_mimo_ndp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_brp_sch_flag_err", + htt_stats_buf->ac_mu_mimo_brp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n\n"); + + stats->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_ax_sched_status_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats) +{ + const struct ath12k_htt_tx_selfgen_ax_sched_status_stats_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats->buf; + u32 len = stats->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_SELFGEN_AX_SCHED_STATUS_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "ax_su_ndpa_sch_status", + htt_stats_buf->ax_su_ndpa_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_su_ndp_sch_status", + htt_stats_buf->ax_su_ndp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_mimo_ndpa_sch_status", + htt_stats_buf->ax_mu_mimo_ndpa_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_mimo_ndp_sch_status", + htt_stats_buf->ax_mu_mimo_ndp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_brp_sch_status", + htt_stats_buf->ax_mu_brp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_bar_sch_status", + htt_stats_buf->ax_mu_bar_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_basic_trig_sch_status", + htt_stats_buf->ax_basic_trig_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_su_ndp_sch_flag_err", + htt_stats_buf->ax_su_ndp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_mimo_ndp_sch_flag_err", + htt_stats_buf->ax_mu_mimo_ndp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_brp_sch_flag_err", + htt_stats_buf->ax_mu_brp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_bar_sch_flag_err", + htt_stats_buf->ax_mu_bar_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ax_basic_trig_sch_flag_err", + htt_stats_buf->ax_basic_trig_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "ax_ulmumimo_trig_sch_status", + htt_stats_buf->ax_ulmumimo_trig_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "ax_ulmumimo_trig_sch_flag_err", + htt_stats_buf->ax_ulmumimo_trig_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n\n"); + + stats->buf_len = len; +} + +static void +ath12k_htt_print_tx_selfgen_be_sched_status_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats) +{ + const struct ath12k_htt_tx_selfgen_be_sched_status_stats_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats->buf; + u32 len = stats->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_SELFGEN_BE_SCHED_STATUS_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "be_su_ndpa_sch_status", + htt_stats_buf->be_su_ndpa_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_su_ndp_sch_status", + htt_stats_buf->be_su_ndp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_mimo_ndpa_sch_status", + htt_stats_buf->be_mu_mimo_ndpa_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_mimo_ndp_sch_status", + htt_stats_buf->be_mu_mimo_ndp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_brp_sch_status", + htt_stats_buf->be_mu_brp_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_bar_sch_status", + htt_stats_buf->be_mu_bar_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_basic_trig_sch_status", + htt_stats_buf->be_basic_trig_sch_status, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_su_ndp_sch_flag_err", + htt_stats_buf->be_su_ndp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_mimo_ndp_sch_flag_err", + htt_stats_buf->be_mu_mimo_ndp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_brp_sch_flag_err", + htt_stats_buf->be_mu_brp_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "be_mu_bar_sch_flag_err", + htt_stats_buf->be_mu_bar_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "be_basic_trig_sch_flag_err", + htt_stats_buf->be_basic_trig_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n"); + len += print_array_to_buf(buf, len, "be_basic_trig_sch_flag_err", + htt_stats_buf->be_basic_trig_sch_flag_err, + ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS, "\n"); + len += print_array_to_buf(buf, len, "be_ulmumimo_trig_sch_flag_err", + htt_stats_buf->be_ulmumimo_trig_sch_flag_err, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS, "\n\n"); + + stats->buf_len = len; +} + +static void +ath12k_htt_print_stats_string_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_stats_string_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i; + u16 index = 0; + u32 datum; + char data[ATH12K_HTT_MAX_STRING_LEN] = {0}; + + tag_len = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:\n"); + for (i = 0; i < tag_len; i++) { + datum = __le32_to_cpu(htt_stats_buf->data[i]); + index += scnprintf(&data[index], ATH12K_HTT_MAX_STRING_LEN - index, + "%.*s", 4, (char *)&datum); + if (index >= ATH12K_HTT_MAX_STRING_LEN) + break; + } + len += scnprintf(buf + len, buf_len - len, "data = %s\n\n", data); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sring_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sring_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + u32 avail_words; + u32 head_tail_ptr; + u32 sring_stat; + u32 tail_ptr; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__ring_id__arena__ep); + avail_words = __le32_to_cpu(htt_stats_buf->num_avail_words__num_valid_words); + head_tail_ptr = __le32_to_cpu(htt_stats_buf->head_ptr__tail_ptr); + sring_stat = __le32_to_cpu(htt_stats_buf->consumer_empty__producer_full); + tail_ptr = __le32_to_cpu(htt_stats_buf->prefetch_count__internal_tail_ptr); + + len += scnprintf(buf + len, buf_len - len, "HTT_SRING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_SRING_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "ring_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_SRING_STATS_RING_ID)); + len += scnprintf(buf + len, buf_len - len, "arena = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_SRING_STATS_ARENA)); + len += scnprintf(buf + len, buf_len - len, "ep = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_SRING_STATS_EP)); + len += scnprintf(buf + len, buf_len - len, "base_addr_lsb = 0x%x\n", + le32_to_cpu(htt_stats_buf->base_addr_lsb)); + len += scnprintf(buf + len, buf_len - len, "base_addr_msb = 0x%x\n", + le32_to_cpu(htt_stats_buf->base_addr_msb)); + len += scnprintf(buf + len, buf_len - len, "ring_size = %u\n", + le32_to_cpu(htt_stats_buf->ring_size)); + len += scnprintf(buf + len, buf_len - len, "elem_size = %u\n", + le32_to_cpu(htt_stats_buf->elem_size)); + len += scnprintf(buf + len, buf_len - len, "num_avail_words = %u\n", + u32_get_bits(avail_words, + ATH12K_HTT_SRING_STATS_NUM_AVAIL_WORDS)); + len += scnprintf(buf + len, buf_len - len, "num_valid_words = %u\n", + u32_get_bits(avail_words, + ATH12K_HTT_SRING_STATS_NUM_VALID_WORDS)); + len += scnprintf(buf + len, buf_len - len, "head_ptr = %u\n", + u32_get_bits(head_tail_ptr, ATH12K_HTT_SRING_STATS_HEAD_PTR)); + len += scnprintf(buf + len, buf_len - len, "tail_ptr = %u\n", + u32_get_bits(head_tail_ptr, ATH12K_HTT_SRING_STATS_TAIL_PTR)); + len += scnprintf(buf + len, buf_len - len, "consumer_empty = %u\n", + u32_get_bits(sring_stat, + ATH12K_HTT_SRING_STATS_CONSUMER_EMPTY)); + len += scnprintf(buf + len, buf_len - len, "producer_full = %u\n", + u32_get_bits(head_tail_ptr, + ATH12K_HTT_SRING_STATS_PRODUCER_FULL)); + len += scnprintf(buf + len, buf_len - len, "prefetch_count = %u\n", + u32_get_bits(tail_ptr, ATH12K_HTT_SRING_STATS_PREFETCH_COUNT)); + len += scnprintf(buf + len, buf_len - len, "internal_tail_ptr = %u\n\n", + u32_get_bits(tail_ptr, + ATH12K_HTT_SRING_STATS_INTERNAL_TAIL_PTR)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sfm_cmn_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sfm_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CMN_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "buf_total = %u\n", + le32_to_cpu(htt_stats_buf->buf_total)); + len += scnprintf(buf + len, buf_len - len, "mem_empty = %u\n", + le32_to_cpu(htt_stats_buf->mem_empty)); + len += scnprintf(buf + len, buf_len - len, "deallocate_bufs = %u\n", + le32_to_cpu(htt_stats_buf->deallocate_bufs)); + len += scnprintf(buf + len, buf_len - len, "num_records = %u\n\n", + le32_to_cpu(htt_stats_buf->num_records)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sfm_client_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sfm_client_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CLIENT_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "client_id = %u\n", + le32_to_cpu(htt_stats_buf->client_id)); + len += scnprintf(buf + len, buf_len - len, "buf_min = %u\n", + le32_to_cpu(htt_stats_buf->buf_min)); + len += scnprintf(buf + len, buf_len - len, "buf_max = %u\n", + le32_to_cpu(htt_stats_buf->buf_max)); + len += scnprintf(buf + len, buf_len - len, "buf_busy = %u\n", + le32_to_cpu(htt_stats_buf->buf_busy)); + len += scnprintf(buf + len, buf_len - len, "buf_alloc = %u\n", + le32_to_cpu(htt_stats_buf->buf_alloc)); + len += scnprintf(buf + len, buf_len - len, "buf_avail = %u\n", + le32_to_cpu(htt_stats_buf->buf_avail)); + len += scnprintf(buf + len, buf_len - len, "num_users = %u\n\n", + le32_to_cpu(htt_stats_buf->num_users)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sfm_client_user_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sfm_client_user_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV:\n"); + len += print_array_to_buf(buf, len, "dwords_used_by_user_n", + htt_stats_buf->dwords_used_by_user_n, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_pdev_mu_mimo_sch_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_SCH_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mu_mimo_sch_posted = %u\n", + le32_to_cpu(htt_stats_buf->mu_mimo_sch_posted)); + len += scnprintf(buf + len, buf_len - len, "mu_mimo_sch_failed = %u\n", + le32_to_cpu(htt_stats_buf->mu_mimo_sch_failed)); + len += scnprintf(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + le32_to_cpu(htt_stats_buf->mu_mimo_ppdu_posted)); + len += scnprintf(buf + len, buf_len - len, + "\nac_mu_mimo_sch_posted_per_group_index %u (SU) = %u\n", 0, + le32_to_cpu(htt_stats_buf->ac_mu_mimo_per_grp_sz[0])); + for (i = 1; i < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_sch_posted_per_group_index %u ", i); + len += scnprintf(buf + len, buf_len - len, + "(TOTAL STREAMS = %u) = %u\n", i + 1, + le32_to_cpu(htt_stats_buf->ac_mu_mimo_per_grp_sz[i])); + } + + for (i = 0; i < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_sch_posted_per_group_index %u ", + i + ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS); + len += scnprintf(buf + len, buf_len - len, + "(TOTAL STREAMS = %u) = %u\n", + i + ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS + 1, + le32_to_cpu(htt_stats_buf->ac_mu_mimo_grp_sz_ext[i])); + } + + len += scnprintf(buf + len, buf_len - len, + "\nax_mu_mimo_sch_posted_per_group_index %u (SU) = %u\n", 0, + le32_to_cpu(htt_stats_buf->ax_mu_mimo_per_grp_sz[0])); + for (i = 1; i < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_sch_posted_per_group_index %u ", i); + len += scnprintf(buf + len, buf_len - len, + "(TOTAL STREAMS = %u) = %u\n", i + 1, + le32_to_cpu(htt_stats_buf->ax_mu_mimo_per_grp_sz[i])); + } + + len += scnprintf(buf + len, buf_len - len, + "\nbe_mu_mimo_sch_posted_per_group_index %u (SU) = %u\n", 0, + le32_to_cpu(htt_stats_buf->be_mu_mimo_per_grp_sz[0])); + for (i = 1; i < ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "be_mu_mimo_sch_posted_per_group_index %u ", i); + len += scnprintf(buf + len, buf_len - len, + "(TOTAL STREAMS = %u) = %u\n", i + 1, + le32_to_cpu(htt_stats_buf->be_mu_mimo_per_grp_sz[i])); + } + + len += scnprintf(buf + len, buf_len - len, "\n11ac MU_MIMO SCH STATS:\n"); + for (i = 0; i < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_sch_nusers_"); + len += scnprintf(buf + len, buf_len - len, "%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ac_mu_mimo_sch_nusers[i])); + } + + len += scnprintf(buf + len, buf_len - len, "\n11ax MU_MIMO SCH STATS:\n"); + for (i = 0; i < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_sch_nusers_"); + len += scnprintf(buf + len, buf_len - len, "%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ax_mu_mimo_sch_nusers[i])); + } + + len += scnprintf(buf + len, buf_len - len, "\n11be MU_MIMO SCH STATS:\n"); + for (i = 0; i < ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, "be_mu_mimo_sch_nusers_"); + len += scnprintf(buf + len, buf_len - len, "%u = %u\n", i, + le32_to_cpu(htt_stats_buf->be_mu_mimo_sch_nusers[i])); + } + + len += scnprintf(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:\n"); + for (i = 0; i < ATH12K_HTT_TX_NUM_OFDMA_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "ax_ofdma_sch_nusers_%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ax_ofdma_sch_nusers[i])); + len += scnprintf(buf + len, buf_len - len, + "ax_ul_ofdma_basic_sch_nusers_%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ax_ul_ofdma_nusers[i])); + len += scnprintf(buf + len, buf_len - len, + "ax_ul_ofdma_bsr_sch_nusers_%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ax_ul_ofdma_bsr_nusers[i])); + len += scnprintf(buf + len, buf_len - len, + "ax_ul_ofdma_bar_sch_nusers_%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ax_ul_ofdma_bar_nusers[i])); + len += scnprintf(buf + len, buf_len - len, + "ax_ul_ofdma_brp_sch_nusers_%u = %u\n\n", i, + le32_to_cpu(htt_stats_buf->ax_ul_ofdma_brp_nusers[i])); + } + + len += scnprintf(buf + len, buf_len - len, "11ax UL MUMIMO SCH STATS:\n"); + for (i = 0; i < ATH12K_HTT_TX_NUM_UL_MUMIMO_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "ax_ul_mumimo_basic_sch_nusers_%u = %u\n", i, + le32_to_cpu(htt_stats_buf->ax_ul_mumimo_nusers[i])); + len += scnprintf(buf + len, buf_len - len, + "ax_ul_mumimo_brp_sch_nusers_%u = %u\n\n", i, + le32_to_cpu(htt_stats_buf->ax_ul_mumimo_brp_nusers[i])); + } + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_pdev_mumimo_grp_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_mumimo_grp_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + int j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_MUMIMO_GRP_STATS:\n"); + len += print_array_to_buf(buf, len, + "dl_mumimo_grp_tputs_observed (per bin = 300 mbps)", + htt_stats_buf->dl_mumimo_grp_tputs, + ATH12K_HTT_STATS_MUMIMO_TPUT_NUM_BINS, "\n"); + len += print_array_to_buf(buf, len, "dl_mumimo_grp eligible", + htt_stats_buf->dl_mumimo_grp_eligible, + ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ, "\n"); + len += print_array_to_buf(buf, len, "dl_mumimo_grp_ineligible", + htt_stats_buf->dl_mumimo_grp_ineligible, + ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ, "\n"); + len += scnprintf(buf + len, buf_len - len, "dl_mumimo_grp_invalid:\n"); + for (j = 0; j < ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ; j++) { + len += scnprintf(buf + len, buf_len - len, "grp_id = %u", j); + len += print_array_to_buf(buf, len, "", + htt_stats_buf->dl_mumimo_grp_invalid, + ATH12K_HTT_STATS_MAX_INVALID_REASON_CODE, + "\n"); + } + + len += print_array_to_buf(buf, len, "ul_mumimo_grp_best_grp_size", + htt_stats_buf->ul_mumimo_grp_best_grp_size, + ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ, "\n"); + len += print_array_to_buf(buf, len, "ul_mumimo_grp_best_num_usrs = ", + htt_stats_buf->ul_mumimo_grp_best_usrs, + ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS, "\n"); + len += print_array_to_buf(buf, len, + "ul_mumimo_grp_tputs_observed (per bin = 300 mbps)", + htt_stats_buf->ul_mumimo_grp_tputs, + ATH12K_HTT_STATS_MUMIMO_TPUT_NUM_BINS, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 user_index; + u32 tx_sched_mode; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + user_index = __le32_to_cpu(htt_stats_buf->user_index); + tx_sched_mode = __le32_to_cpu(htt_stats_buf->tx_sched_mode); + + if (tx_sched_mode == ATH12K_HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC) { + if (!user_index) + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AC_MPDU_STATS:\n"); + + if (user_index < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS) { + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_queued_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_queued_usr)); + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_tried_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_tried_usr)); + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_failed_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_failed_usr)); + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_requeued_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_requeued_usr)); + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_err_no_ba_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->err_no_ba_usr)); + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_mpdu_underrun_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdu_underrun_usr)); + len += scnprintf(buf + len, buf_len - len, + "ac_mu_mimo_ampdu_underrun_usr_%u = %u\n\n", + user_index, + le32_to_cpu(htt_stats_buf->ampdu_underrun_usr)); + } + } + + if (tx_sched_mode == ATH12K_HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX) { + if (!user_index) + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AX_MPDU_STATS:\n"); + + if (user_index < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS) { + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_queued_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_queued_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_tried_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_tried_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_failed_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_failed_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_requeued_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_requeued_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_err_no_ba_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->err_no_ba_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_mpdu_underrun_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdu_underrun_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_mimo_ampdu_underrun_usr_%u = %u\n\n", + user_index, + le32_to_cpu(htt_stats_buf->ampdu_underrun_usr)); + } + } + + if (tx_sched_mode == ATH12K_HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX) { + if (!user_index) + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_AX_MU_OFDMA_MPDU_STATS:\n"); + + if (user_index < ATH12K_HTT_TX_NUM_OFDMA_USER_STATS) { + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_queued_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_queued_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_tried_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_tried_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_failed_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_failed_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_requeued_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdus_requeued_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_err_no_ba_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->err_no_ba_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_mpdu_underrun_usr_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->mpdu_underrun_usr)); + len += scnprintf(buf + len, buf_len - len, + "ax_mu_ofdma_ampdu_underrun_usr_%u = %u\n\n", + user_index, + le32_to_cpu(htt_stats_buf->ampdu_underrun_usr)); + } + } + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_pdev_cca_stats_hist_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_cca_stats_hist_v1_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_CCA_STATS_HIST_TLV :\n"); + len += scnprintf(buf + len, buf_len - len, "chan_num = %u\n", + le32_to_cpu(htt_stats_buf->chan_num)); + len += scnprintf(buf + len, buf_len - len, "num_records = %u\n", + le32_to_cpu(htt_stats_buf->num_records)); + len += scnprintf(buf + len, buf_len - len, "valid_cca_counters_bitmap = 0x%x\n", + le32_to_cpu(htt_stats_buf->valid_cca_counters_bitmap)); + len += scnprintf(buf + len, buf_len - len, "collection_interval = %u\n\n", + le32_to_cpu(htt_stats_buf->collection_interval)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_stats_cca_counters_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_PDEV_STATS_CCA_COUNTERS_TLV:(in usec)\n"); + len += scnprintf(buf + len, buf_len - len, "tx_frame_usec = %u\n", + le32_to_cpu(htt_stats_buf->tx_frame_usec)); + len += scnprintf(buf + len, buf_len - len, "rx_frame_usec = %u\n", + le32_to_cpu(htt_stats_buf->rx_frame_usec)); + len += scnprintf(buf + len, buf_len - len, "rx_clear_usec = %u\n", + le32_to_cpu(htt_stats_buf->rx_clear_usec)); + len += scnprintf(buf + len, buf_len - len, "my_rx_frame_usec = %u\n", + le32_to_cpu(htt_stats_buf->my_rx_frame_usec)); + len += scnprintf(buf + len, buf_len - len, "usec_cnt = %u\n", + le32_to_cpu(htt_stats_buf->usec_cnt)); + len += scnprintf(buf + len, buf_len - len, "med_rx_idle_usec = %u\n", + le32_to_cpu(htt_stats_buf->med_rx_idle_usec)); + len += scnprintf(buf + len, buf_len - len, "med_tx_idle_global_usec = %u\n", + le32_to_cpu(htt_stats_buf->med_tx_idle_global_usec)); + len += scnprintf(buf + len, buf_len - len, "cca_obss_usec = %u\n\n", + le32_to_cpu(htt_stats_buf->cca_obss_usec)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_sounding_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf; + const __le32 *cbf_20, *cbf_40, *cbf_80, *cbf_160, *cbf_320; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 tx_sounding_mode; + u8 i, u; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + cbf_20 = htt_stats_buf->cbf_20; + cbf_40 = htt_stats_buf->cbf_40; + cbf_80 = htt_stats_buf->cbf_80; + cbf_160 = htt_stats_buf->cbf_160; + cbf_320 = htt_stats_buf->cbf_320; + tx_sounding_mode = le32_to_cpu(htt_stats_buf->tx_sounding_mode); + + if (tx_sounding_mode == ATH12K_HTT_TX_AC_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_AC_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_AX_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_BE_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nHTT_TX_BE_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_320 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_320[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, + "160MHz: %u, 320MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++]), + le32_to_cpu(htt_stats_buf->sounding_320[u])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_CMN_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nCV UPLOAD HANDLER STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_nc_mismatch_err)); + len += scnprintf(buf + len, buf_len - len, "cv_fcs_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_fcs_err)); + len += scnprintf(buf + len, buf_len - len, "cv_frag_idx_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_frag_idx_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_invalid_peer_id = %u\n", + le32_to_cpu(htt_stats_buf->cv_invalid_peer_id)); + len += scnprintf(buf + len, buf_len - len, "cv_no_txbf_setup = %u\n", + le32_to_cpu(htt_stats_buf->cv_no_txbf_setup)); + len += scnprintf(buf + len, buf_len - len, "cv_expiry_in_update = %u\n", + le32_to_cpu(htt_stats_buf->cv_expiry_in_update)); + len += scnprintf(buf + len, buf_len - len, "cv_pkt_bw_exceed = %u\n", + le32_to_cpu(htt_stats_buf->cv_pkt_bw_exceed)); + len += scnprintf(buf + len, buf_len - len, "cv_dma_not_done_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_dma_not_done_err)); + len += scnprintf(buf + len, buf_len - len, "cv_update_failed = %u\n", + le32_to_cpu(htt_stats_buf->cv_update_failed)); + len += scnprintf(buf + len, buf_len - len, "cv_dma_timeout_error = %u\n", + le32_to_cpu(htt_stats_buf->cv_dma_timeout_error)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_ibf_uploads = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_ibf_uploads)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_ebf_uploads = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_ebf_uploads)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_received = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_received)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_fed_back = %u\n\n", + le32_to_cpu(htt_stats_buf->cv_buf_fed_back)); + + len += scnprintf(buf + len, buf_len - len, "CV QUERY STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "cv_total_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_query)); + len += scnprintf(buf + len, buf_len - len, + "cv_total_pattern_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_pattern_query)); + len += scnprintf(buf + len, buf_len - len, "cv_total_bw_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_bw_query)); + len += scnprintf(buf + len, buf_len - len, "cv_invalid_bw_coding = %u\n", + le32_to_cpu(htt_stats_buf->cv_invalid_bw_coding)); + len += scnprintf(buf + len, buf_len - len, "cv_forced_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_forced_sounding)); + len += scnprintf(buf + len, buf_len - len, + "cv_standalone_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_standalone_sounding)); + len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_nc_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_fb_type_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_fb_type_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_ofdma_bw_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_ofdma_bw_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_bw_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_bw_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_pattern_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_pattern_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_preamble_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_preamble_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_nr_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_nr_mismatch)); + len += scnprintf(buf + len, buf_len - len, + "cv_in_use_cnt_exceeded = %u\n", + le32_to_cpu(htt_stats_buf->cv_in_use_cnt_exceeded)); + len += scnprintf(buf + len, buf_len - len, "cv_ntbr_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_ntbr_sounding)); + len += scnprintf(buf + len, buf_len - len, + "cv_found_upload_in_progress = %u\n", + le32_to_cpu(htt_stats_buf->cv_found_upload_in_progress)); + len += scnprintf(buf + len, buf_len - len, + "cv_expired_during_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_expired_during_query)); + len += scnprintf(buf + len, buf_len - len, "cv_found = %u\n", + le32_to_cpu(htt_stats_buf->cv_found)); + len += scnprintf(buf + len, buf_len - len, "cv_not_found = %u\n", + le32_to_cpu(htt_stats_buf->cv_not_found)); + len += scnprintf(buf + len, buf_len - len, "cv_total_query_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_query_ibf)); + len += scnprintf(buf + len, buf_len - len, "cv_found_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_found_ibf)); + len += scnprintf(buf + len, buf_len - len, "cv_not_found_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_not_found_ibf)); + len += scnprintf(buf + len, buf_len - len, + "cv_expired_during_query_ibf = %u\n\n", + le32_to_cpu(htt_stats_buf->cv_expired_during_query_ibf)); + } + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_obss_pd_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i; + static const char *access_cat_names[ATH12K_HTT_NUM_AC_WMM] = {"best effort", + "background", + "video", "voice"}; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_OBSS_PD_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "num_spatial_reuse_tx = %u\n", + le32_to_cpu(htt_stats_buf->num_sr_tx_transmissions)); + len += scnprintf(buf + len, buf_len - len, + "num_spatial_reuse_opportunities = %u\n", + le32_to_cpu(htt_stats_buf->num_spatial_reuse_opportunities)); + len += scnprintf(buf + len, buf_len - len, "num_non_srg_opportunities = %u\n", + le32_to_cpu(htt_stats_buf->num_non_srg_opportunities)); + len += scnprintf(buf + len, buf_len - len, "num_non_srg_ppdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->num_non_srg_ppdu_tried)); + len += scnprintf(buf + len, buf_len - len, "num_non_srg_ppdu_success = %u\n", + le32_to_cpu(htt_stats_buf->num_non_srg_ppdu_success)); + len += scnprintf(buf + len, buf_len - len, "num_srg_opportunities = %u\n", + le32_to_cpu(htt_stats_buf->num_srg_opportunities)); + len += scnprintf(buf + len, buf_len - len, "num_srg_ppdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->num_srg_ppdu_tried)); + len += scnprintf(buf + len, buf_len - len, "num_srg_ppdu_success = %u\n", + le32_to_cpu(htt_stats_buf->num_srg_ppdu_success)); + len += scnprintf(buf + len, buf_len - len, "num_psr_opportunities = %u\n", + le32_to_cpu(htt_stats_buf->num_psr_opportunities)); + len += scnprintf(buf + len, buf_len - len, "num_psr_ppdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->num_psr_ppdu_tried)); + len += scnprintf(buf + len, buf_len - len, "num_psr_ppdu_success = %u\n", + le32_to_cpu(htt_stats_buf->num_psr_ppdu_success)); + len += scnprintf(buf + len, buf_len - len, "min_duration_check_flush_cnt = %u\n", + le32_to_cpu(htt_stats_buf->num_obss_min_dur_check_flush_cnt)); + len += scnprintf(buf + len, buf_len - len, "sr_ppdu_abort_flush_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->num_sr_ppdu_abort_flush_cnt)); + + len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_OBSS_PD_PER_AC_STATS:\n"); + for (i = 0; i < ATH12K_HTT_NUM_AC_WMM; i++) { + len += scnprintf(buf + len, buf_len - len, "Access Category %u (%s)\n", + i, access_cat_names[i]); + len += scnprintf(buf + len, buf_len - len, + "num_non_srg_ppdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->num_non_srg_tried_per_ac[i])); + len += scnprintf(buf + len, buf_len - len, + "num_non_srg_ppdu_success = %u\n", + le32_to_cpu(htt_stats_buf->num_non_srg_success_ac[i])); + len += scnprintf(buf + len, buf_len - len, "num_srg_ppdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->num_srg_tried_per_ac[i])); + len += scnprintf(buf + len, buf_len - len, + "num_srg_ppdu_success = %u\n\n", + le32_to_cpu(htt_stats_buf->num_srg_success_per_ac[i])); + } + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_ctx_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_ctx_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CTX_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "duration = %u\n", + le32_to_cpu(htt_stats_buf->duration)); + len += scnprintf(buf + len, buf_len - len, "tx_msdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tx_msdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "tx_mpdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tx_mpdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_msdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_msdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_mpdu_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->rx_mpdu_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_cnt(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_cnt_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CNT_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "prof_enable_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->prof_enable_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + if (le32_to_cpu(htt_stats_buf->print_header) == 1) { + len += scnprintf(buf + len, buf_len - len, + "HTT_STATS_LATENCY_PROF_TLV:\n"); + } + + len += scnprintf(buf + len, buf_len - len, "Latency name = %s\n", + htt_stats_buf->latency_prof_name); + len += scnprintf(buf + len, buf_len - len, "count = %u\n", + le32_to_cpu(htt_stats_buf->cnt)); + len += scnprintf(buf + len, buf_len - len, "minimum = %u\n", + le32_to_cpu(htt_stats_buf->min)); + len += scnprintf(buf + len, buf_len - len, "maximum = %u\n", + le32_to_cpu(htt_stats_buf->max)); + len += scnprintf(buf + len, buf_len - len, "last = %u\n", + le32_to_cpu(htt_stats_buf->last)); + len += scnprintf(buf + len, buf_len - len, "total = %u\n", + le32_to_cpu(htt_stats_buf->tot)); + len += scnprintf(buf + len, buf_len - len, "average = %u\n", + le32_to_cpu(htt_stats_buf->avg)); + len += scnprintf(buf + len, buf_len - len, "histogram interval = %u\n", + le32_to_cpu(htt_stats_buf->hist_intvl)); + len += print_array_to_buf(buf, len, "histogram", htt_stats_buf->hist, + ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_ofdma_trigger_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_id; + u8 j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_TRIGGER_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma)); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs", + htt_stats_buf->ul_ofdma_rx_mcs, + ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n"); + for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) { + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u]", j); + len += print_array_to_buf(buf, len, "", + htt_stats_buf->ul_ofdma_rx_gi[j], + ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n"); + } + + len += print_array_to_buf_index(buf, len, "ul_ofdma_rx_nss", 1, + htt_stats_buf->ul_ofdma_rx_nss, + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw", + htt_stats_buf->ul_ofdma_rx_bw, + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; j++) { + len += scnprintf(buf + len, buf_len - len, j == 0 ? + "half_ul_ofdma_rx_bw" : + "quarter_ul_ofdma_rx_bw"); + len += print_array_to_buf(buf, len, "", htt_stats_buf->red_bw[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc)); + + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ru_size_ppdu = "); + for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_ax_tx_rx_ru_size_to_str(j), + le32_to_cpu(htt_stats_buf->data_ru_size_ppdu[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, + "rx_ulofdma_non_data_ru_size_ppdu = "); + for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_ax_tx_rx_ru_size_to_str(j), + le32_to_cpu(htt_stats_buf->non_data_ru_size_ppdu[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "rx_rssi_track_sta_aid", + htt_stats_buf->uplink_sta_aid, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_target_rssi", + htt_stats_buf->uplink_sta_target_rssi, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_fd_rssi", + htt_stats_buf->uplink_sta_fd_rssi, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_power_headroom", + htt_stats_buf->uplink_sta_power_headroom, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_basic_trigger_rx_qos_null_only = %u\n\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_bsc_trig_rx_qos_null_only)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_ofdma_user_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 user_index; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + user_index = __le32_to_cpu(htt_stats_buf->user_index); + + if (!user_index) + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_OFDMA_USER_STAS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_ppdu)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ppdu_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_data_ppdu)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_ok)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_fail)); + len += scnprintf(buf + len, buf_len - len, + "rx_ulofdma_non_data_nusers_%u = %u\n", user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_nusers)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_nusers_%u = %u\n\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_data_nusers)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_mumimo_trig_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv *htt_stats_buf = tag_buf; + char str_buf[ATH12K_HTT_MAX_STRING_LEN] = {0}; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_id; + u16 index; + u8 i, j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_MUMIMO_TRIG_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_mumimo)); + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i, + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs[i])); + + for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS, + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs_ext[i])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_mcs = %s\n", str_buf); + + for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) { + index = 0; + memset(&str_buf[index], 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i, + le32_to_cpu(htt_stats_buf->ul_rx_gi[j][i])); + + for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS, + le32_to_cpu(htt_stats_buf->ul_gi_ext[j][i])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_rx_gi_%u = %s\n", j, str_buf); + } + + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + len += print_array_to_buf_index(buf, len, "ul_mumimo_rx_nss", 1, + htt_stats_buf->ul_mumimo_rx_nss, + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + + len += print_array_to_buf(buf, len, "ul_mumimo_rx_bw", + htt_stats_buf->ul_mumimo_rx_bw, + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; i++) { + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (j = 0; j < ATH12K_HTT_RX_NUM_BW_CNTRS; j++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", j, + le32_to_cpu(htt_stats_buf->red_bw[i][j])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, "%s = %s\n", + i == 0 ? "half_ul_mumimo_rx_bw" : + "quarter_ul_mumimo_rx_bw", str_buf); + } + + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_ldpc)); + + for (j = 0; j < ATH12K_HTT_RX_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_rssi_in_dbm: chain%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->ul_rssi[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_target_rssi: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->tgt_rssi[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_fd_rssi: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->fd[j], + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ulmumimo_pilot_evm_db_mean: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->db[j], + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + } + + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_basic_trigger_rx_qos_null_only = %u\n\n", + le32_to_cpu(htt_stats_buf->mumimo_bsc_trig_rx_qos_null_only)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_rx_fse_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_fse_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_RX_FSE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "=== Software RX FSE STATS ===\n"); + len += scnprintf(buf + len, buf_len - len, "Enable count = %u\n", + le32_to_cpu(htt_stats_buf->fse_enable_cnt)); + len += scnprintf(buf + len, buf_len - len, "Disable count = %u\n", + le32_to_cpu(htt_stats_buf->fse_disable_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache invalidate entry count = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_invalidate_entry_cnt)); + len += scnprintf(buf + len, buf_len - len, "Full cache invalidate count = %u\n", + le32_to_cpu(htt_stats_buf->fse_full_cache_invalidate_cnt)); + + len += scnprintf(buf + len, buf_len - len, "\n=== Hardware RX FSE STATS ===\n"); + len += scnprintf(buf + len, buf_len - len, "Cache hits count = %u\n", + le32_to_cpu(htt_stats_buf->fse_num_cache_hits_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache no. of searches = %u\n", + le32_to_cpu(htt_stats_buf->fse_num_searches_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache occupancy peak count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[5]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[6])); + len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[7]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[8])); + len += scnprintf(buf + len, buf_len - len, "[128] = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[9])); + len += scnprintf(buf + len, buf_len - len, "Cache occupancy current count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[5]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[6])); + len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[7]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[8])); + len += scnprintf(buf + len, buf_len - len, "[128] = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[9])); + len += scnprintf(buf + len, buf_len - len, "Cache search square count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-50] = %u [51-100] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[101-200] = %u [201-255] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[256] = %u\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[5])); + len += scnprintf(buf + len, buf_len - len, "Cache search peak pending count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[3])); + len += scnprintf(buf + len, buf_len - len, "Cache search tot pending count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[3])); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_txrate_txbf_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u8 i; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_STATS_PDEV_TX_RATE_TXBF_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "Legacy OFDM Rates: 6 Mbps: %u, ", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[0])); + len += scnprintf(buf + len, buf_len - len, "9 Mbps: %u, 12 Mbps: %u, ", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[2])); + len += scnprintf(buf + len, buf_len - len, "18 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[3])); + len += scnprintf(buf + len, buf_len - len, "24 Mbps: %u, 36 Mbps: %u, ", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[4]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[5])); + len += scnprintf(buf + len, buf_len - len, "48 Mbps: %u, 54 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[6]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[7])); + + len += print_array_to_buf(buf, len, "tx_ol_mcs", htt_stats_buf->tx_su_ol_mcs, + ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_ibf_mcs", htt_stats_buf->tx_su_ibf_mcs, + ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_txbf_mcs", htt_stats_buf->tx_su_txbf_mcs, + ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf_index(buf, len, "tx_ol_nss", 1, + htt_stats_buf->tx_su_ol_nss, + ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, + "\n"); + len += print_array_to_buf_index(buf, len, "tx_ibf_nss", 1, + htt_stats_buf->tx_su_ibf_nss, + ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, + "\n"); + len += print_array_to_buf_index(buf, len, "tx_txbf_nss", 1, + htt_stats_buf->tx_su_txbf_nss, + ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, + "\n"); + len += print_array_to_buf(buf, len, "tx_ol_bw", htt_stats_buf->tx_su_ol_bw, + ATH12K_HTT_TXBF_NUM_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES; i++) + len += print_array_to_buf(buf, len, i ? "quarter_tx_ol_bw" : + "half_tx_ol_bw", + htt_stats_buf->ol[i], + ATH12K_HTT_TXBF_NUM_BW_CNTRS, + "\n"); + + len += print_array_to_buf(buf, len, "tx_ibf_bw", htt_stats_buf->tx_su_ibf_bw, + ATH12K_HTT_TXBF_NUM_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES; i++) + len += print_array_to_buf(buf, len, i ? "quarter_tx_ibf_bw" : + "half_tx_ibf_bw", + htt_stats_buf->ibf[i], + ATH12K_HTT_TXBF_NUM_BW_CNTRS, + "\n"); + + len += print_array_to_buf(buf, len, "tx_txbf_bw", htt_stats_buf->tx_su_txbf_bw, + ATH12K_HTT_TXBF_NUM_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES; i++) + len += print_array_to_buf(buf, len, i ? "quarter_tx_txbf_bw" : + "half_tx_txbf_bw", + htt_stats_buf->txbf[i], + ATH12K_HTT_TXBF_NUM_BW_CNTRS, + "\n"); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, + "HTT_STATS_PDEV_TXBF_FLAG_RETURN_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "TXBF_reason_code_stats: 0:%u, 1:%u,", + le32_to_cpu(htt_stats_buf->txbf_flag_set_mu_mode), + le32_to_cpu(htt_stats_buf->txbf_flag_set_final_status)); + len += scnprintf(buf + len, buf_len - len, " 2:%u, 3:%u, 4:%u, 5:%u, ", + le32_to_cpu(htt_stats_buf->txbf_flag_not_set_verified_txbf_mode), + le32_to_cpu(htt_stats_buf->txbf_flag_not_set_disable_p2p_access), + le32_to_cpu(htt_stats_buf->txbf_flag_not_set_max_nss_in_he160), + le32_to_cpu(htt_stats_buf->txbf_flag_not_set_disable_uldlofdma)); + len += scnprintf(buf + len, buf_len - len, "6:%u, 7:%u\n\n", + le32_to_cpu(htt_stats_buf->txbf_flag_not_set_mcs_threshold_val), + le32_to_cpu(htt_stats_buf->txbf_flag_not_set_final_status)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_txbf_ofdma_ax_ndpa_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_txbf_ofdma_ax_ndpa_stats_tlv *stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 num_elements; + u8 i; + + if (tag_len < sizeof(*stats_buf)) + return; + + num_elements = le32_to_cpu(stats_buf->num_elems_ax_ndpa_arr); + + len += scnprintf(buf + len, buf_len - len, "HTT_TXBF_OFDMA_AX_NDPA_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ax_ofdma_ndpa_queued ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndpa[i].ax_ofdma_ndpa_queued)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_ndpa_tried ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndpa[i].ax_ofdma_ndpa_tried)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_ndpa_flushed ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndpa[i].ax_ofdma_ndpa_flush)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_ndpa_err ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndpa[i].ax_ofdma_ndpa_err)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_txbf_ofdma_ax_ndp_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_txbf_ofdma_ax_ndp_stats_tlv *stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 num_elements; + u8 i; + + if (tag_len < sizeof(*stats_buf)) + return; + + num_elements = le32_to_cpu(stats_buf->num_elems_ax_ndp_arr); + + len += scnprintf(buf + len, buf_len - len, "HTT_TXBF_OFDMA_AX_NDP_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ax_ofdma_ndp_queued ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndp[i].ax_ofdma_ndp_queued)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_ndp_tried ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndp[i].ax_ofdma_ndp_tried)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_ndp_flushed ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndp[i].ax_ofdma_ndp_flush)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_ndp_err ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_ndp[i].ax_ofdma_ndp_err)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_txbf_ofdma_ax_brp_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_txbf_ofdma_ax_brp_stats_tlv *stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 num_elements; + u8 i; + + if (tag_len < sizeof(*stats_buf)) + return; + + num_elements = le32_to_cpu(stats_buf->num_elems_ax_brp_arr); + + len += scnprintf(buf + len, buf_len - len, "HTT_TXBF_OFDMA_AX_BRP_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ax_ofdma_brpoll_queued ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_brp[i].ax_ofdma_brp_queued)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_brpoll_tied ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_brp[i].ax_ofdma_brp_tried)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_brpoll_flushed ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_brp[i].ax_ofdma_brp_flushed)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_brp_err ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_brp[i].ax_ofdma_brp_err)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_brp_err_num_cbf_rcvd ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_brp[i].ax_ofdma_num_cbf_rcvd)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_txbf_ofdma_ax_steer_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_txbf_ofdma_ax_steer_stats_tlv *stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 num_elements; + u8 i; + + if (tag_len < sizeof(*stats_buf)) + return; + + num_elements = le32_to_cpu(stats_buf->num_elems_ax_steer_arr); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TXBF_OFDMA_AX_STEER_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ax_ofdma_num_ppdu_steer ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_steer[i].num_ppdu_steer)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_num_usrs_prefetch ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_steer[i].num_usr_prefetch)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_num_usrs_sound ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_steer[i].num_usr_sound)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\nax_ofdma_num_usrs_force_sound ="); + for (i = 0; i < num_elements; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", i + 1, + le32_to_cpu(stats_buf->ax_steer[i].num_usr_force_sound)); + len--; + *(buf + len) = '\0'; + + len += scnprintf(buf + len, buf_len - len, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_txbf_ofdma_ax_steer_mpdu_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_txbf_ofdma_ax_steer_mpdu_stats_tlv *stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TXBF_OFDMA_AX_STEER_MPDU_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rbo_steer_mpdus_tried = %u\n", + le32_to_cpu(stats_buf->ax_ofdma_rbo_steer_mpdus_tried)); + len += scnprintf(buf + len, buf_len - len, "rbo_steer_mpdus_failed = %u\n", + le32_to_cpu(stats_buf->ax_ofdma_rbo_steer_mpdus_failed)); + len += scnprintf(buf + len, buf_len - len, "sifs_steer_mpdus_tried = %u\n", + le32_to_cpu(stats_buf->ax_ofdma_sifs_steer_mpdus_tried)); + len += scnprintf(buf + len, buf_len - len, "sifs_steer_mpdus_failed = %u\n\n", + le32_to_cpu(stats_buf->ax_ofdma_sifs_steer_mpdus_failed)); + + stats_req->buf_len = len; +} + +static void ath12k_htt_print_dlpager_entry(const struct ath12k_htt_pgs_info *pg_info, + int idx, char *str_buf) +{ + u64 page_timestamp; + u16 index = 0; + + page_timestamp = ath12k_le32hilo_to_u64(pg_info->ts_msb, pg_info->ts_lsb); + + index += snprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + "Index - %u ; Page Number - %u ; ", + idx, le32_to_cpu(pg_info->page_num)); + index += snprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + "Num of pages - %u ; Timestamp - %lluus\n", + le32_to_cpu(pg_info->num_pgs), page_timestamp); +} + +static void +ath12k_htt_print_dlpager_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_dl_pager_stats_tlv *stat_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 dword_lock, dword_unlock; + int i; + u8 *buf = stats_req->buf; + u8 pg_locked; + u8 pg_unlock; + char str_buf[ATH12K_HTT_MAX_STRING_LEN] = {0}; + + if (tag_len < sizeof(*stat_buf)) + return; + + dword_lock = le32_get_bits(stat_buf->info2, + ATH12K_HTT_DLPAGER_TOTAL_LOCK_PAGES_INFO2); + dword_unlock = le32_get_bits(stat_buf->info2, + ATH12K_HTT_DLPAGER_TOTAL_FREE_PAGES_INFO2); + + pg_locked = ATH12K_HTT_STATS_PAGE_LOCKED; + pg_unlock = ATH12K_HTT_STATS_PAGE_UNLOCKED; + + len += scnprintf(buf + len, buf_len - len, "HTT_DLPAGER_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ASYNC locked pages = %u\n", + le32_get_bits(stat_buf->info0, + ATH12K_HTT_DLPAGER_ASYNC_LOCK_PG_CNT_INFO0)); + len += scnprintf(buf + len, buf_len - len, "SYNC locked pages = %u\n", + le32_get_bits(stat_buf->info0, + ATH12K_HTT_DLPAGER_SYNC_LOCK_PG_CNT_INFO0)); + len += scnprintf(buf + len, buf_len - len, "Total locked pages = %u\n", + le32_get_bits(stat_buf->info1, + ATH12K_HTT_DLPAGER_TOTAL_LOCK_PAGES_INFO1)); + len += scnprintf(buf + len, buf_len - len, "Total free pages = %u\n", + le32_get_bits(stat_buf->info1, + ATH12K_HTT_DLPAGER_TOTAL_FREE_PAGES_INFO1)); + + len += scnprintf(buf + len, buf_len - len, "\nLOCKED PAGES HISTORY\n"); + len += scnprintf(buf + len, buf_len - len, "last_locked_page_idx = %u\n", + dword_lock ? dword_lock - 1 : (ATH12K_PAGER_MAX - 1)); + + for (i = 0; i < ATH12K_PAGER_MAX; i++) { + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + ath12k_htt_print_dlpager_entry(&stat_buf->pgs_info[pg_locked][i], + i, str_buf); + len += scnprintf(buf + len, buf_len - len, "%s", str_buf); + } + + len += scnprintf(buf + len, buf_len - len, "\nUNLOCKED PAGES HISTORY\n"); + len += scnprintf(buf + len, buf_len - len, "last_unlocked_page_idx = %u\n", + dword_unlock ? dword_unlock - 1 : ATH12K_PAGER_MAX - 1); + + for (i = 0; i < ATH12K_PAGER_MAX; i++) { + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + ath12k_htt_print_dlpager_entry(&stat_buf->pgs_info[pg_unlock][i], + i, str_buf); + len += scnprintf(buf + len, buf_len - len, "%s", str_buf); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_phy_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_phy_stats_tlv *htt_stats_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 *buf = stats_req->buf, i; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PHY_STATS_TLV:\n"); + for (i = 0; i < ATH12K_HTT_STATS_MAX_CHAINS; i++) + len += scnprintf(buf + len, buf_len - len, "bdf_nf_chain[%d] = %d\n", + i, a_sle32_to_cpu(htt_stats_buf->nf_chain[i])); + for (i = 0; i < ATH12K_HTT_STATS_MAX_CHAINS; i++) + len += scnprintf(buf + len, buf_len - len, "runtime_nf_chain[%d] = %d\n", + i, a_sle32_to_cpu(htt_stats_buf->runtime_nf_chain[i])); + len += scnprintf(buf + len, buf_len - len, "false_radar_cnt = %u / %u (mins)\n", + le32_to_cpu(htt_stats_buf->false_radar_cnt), + le32_to_cpu(htt_stats_buf->fw_run_time)); + len += scnprintf(buf + len, buf_len - len, "radar_cs_cnt = %u\n", + le32_to_cpu(htt_stats_buf->radar_cs_cnt)); + len += scnprintf(buf + len, buf_len - len, "ani_level = %d\n\n", + a_sle32_to_cpu(htt_stats_buf->ani_level)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_phy_counters_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_phy_counters_tlv *htt_stats_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PHY_COUNTERS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rx_ofdma_timing_err_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_ofdma_timing_err_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_cck_fail_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_cck_fail_cnt)); + len += scnprintf(buf + len, buf_len - len, "mactx_abort_cnt = %u\n", + le32_to_cpu(htt_stats_buf->mactx_abort_cnt)); + len += scnprintf(buf + len, buf_len - len, "macrx_abort_cnt = %u\n", + le32_to_cpu(htt_stats_buf->macrx_abort_cnt)); + len += scnprintf(buf + len, buf_len - len, "phytx_abort_cnt = %u\n", + le32_to_cpu(htt_stats_buf->phytx_abort_cnt)); + len += scnprintf(buf + len, buf_len - len, "phyrx_abort_cnt = %u\n", + le32_to_cpu(htt_stats_buf->phyrx_abort_cnt)); + len += scnprintf(buf + len, buf_len - len, "phyrx_defer_abort_cnt = %u\n", + le32_to_cpu(htt_stats_buf->phyrx_defer_abort_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_gain_adj_lstf_event_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_gain_adj_lstf_event_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_gain_adj_non_legacy_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_gain_adj_non_legacy_cnt)); + len += print_array_to_buf(buf, len, "rx_pkt_cnt", htt_stats_buf->rx_pkt_cnt, + ATH12K_HTT_MAX_RX_PKT_CNT, "\n"); + len += print_array_to_buf(buf, len, "rx_pkt_crc_pass_cnt", + htt_stats_buf->rx_pkt_crc_pass_cnt, + ATH12K_HTT_MAX_RX_PKT_CRC_PASS_CNT, "\n"); + len += print_array_to_buf(buf, len, "per_blk_err_cnt", + htt_stats_buf->per_blk_err_cnt, + ATH12K_HTT_MAX_PER_BLK_ERR_CNT, "\n"); + len += print_array_to_buf(buf, len, "rx_ota_err_cnt", + htt_stats_buf->rx_ota_err_cnt, + ATH12K_HTT_MAX_RX_OTA_ERR_CNT, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_phy_reset_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_phy_reset_stats_tlv *htt_stats_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PHY_RESET_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", + le32_to_cpu(htt_stats_buf->pdev_id)); + len += scnprintf(buf + len, buf_len - len, "chan_mhz = %u\n", + le32_to_cpu(htt_stats_buf->chan_mhz)); + len += scnprintf(buf + len, buf_len - len, "chan_band_center_freq1 = %u\n", + le32_to_cpu(htt_stats_buf->chan_band_center_freq1)); + len += scnprintf(buf + len, buf_len - len, "chan_band_center_freq2 = %u\n", + le32_to_cpu(htt_stats_buf->chan_band_center_freq2)); + len += scnprintf(buf + len, buf_len - len, "chan_phy_mode = %u\n", + le32_to_cpu(htt_stats_buf->chan_phy_mode)); + len += scnprintf(buf + len, buf_len - len, "chan_flags = 0x%0x\n", + le32_to_cpu(htt_stats_buf->chan_flags)); + len += scnprintf(buf + len, buf_len - len, "chan_num = %u\n", + le32_to_cpu(htt_stats_buf->chan_num)); + len += scnprintf(buf + len, buf_len - len, "reset_cause = 0x%0x\n", + le32_to_cpu(htt_stats_buf->reset_cause)); + len += scnprintf(buf + len, buf_len - len, "prev_reset_cause = 0x%0x\n", + le32_to_cpu(htt_stats_buf->prev_reset_cause)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_src = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phy_warm_reset_src)); + len += scnprintf(buf + len, buf_len - len, "rx_gain_tbl_mode = %d\n", + le32_to_cpu(htt_stats_buf->rx_gain_tbl_mode)); + len += scnprintf(buf + len, buf_len - len, "xbar_val = 0x%0x\n", + le32_to_cpu(htt_stats_buf->xbar_val)); + len += scnprintf(buf + len, buf_len - len, "force_calibration = %u\n", + le32_to_cpu(htt_stats_buf->force_calibration)); + len += scnprintf(buf + len, buf_len - len, "phyrf_mode = %u\n", + le32_to_cpu(htt_stats_buf->phyrf_mode)); + len += scnprintf(buf + len, buf_len - len, "phy_homechan = %u\n", + le32_to_cpu(htt_stats_buf->phy_homechan)); + len += scnprintf(buf + len, buf_len - len, "phy_tx_ch_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phy_tx_ch_mask)); + len += scnprintf(buf + len, buf_len - len, "phy_rx_ch_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phy_rx_ch_mask)); + len += scnprintf(buf + len, buf_len - len, "phybb_ini_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phybb_ini_mask)); + len += scnprintf(buf + len, buf_len - len, "phyrf_ini_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phyrf_ini_mask)); + len += scnprintf(buf + len, buf_len - len, "phy_dfs_en_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phy_dfs_en_mask)); + len += scnprintf(buf + len, buf_len - len, "phy_sscan_en_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phy_sscan_en_mask)); + len += scnprintf(buf + len, buf_len - len, "phy_synth_sel_mask = 0x%0x\n", + le32_to_cpu(htt_stats_buf->phy_synth_sel_mask)); + len += scnprintf(buf + len, buf_len - len, "phy_adfs_freq = %u\n", + le32_to_cpu(htt_stats_buf->phy_adfs_freq)); + len += scnprintf(buf + len, buf_len - len, "cck_fir_settings = 0x%0x\n", + le32_to_cpu(htt_stats_buf->cck_fir_settings)); + len += scnprintf(buf + len, buf_len - len, "phy_dyn_pri_chan = %u\n", + le32_to_cpu(htt_stats_buf->phy_dyn_pri_chan)); + len += scnprintf(buf + len, buf_len - len, "cca_thresh = 0x%0x\n", + le32_to_cpu(htt_stats_buf->cca_thresh)); + len += scnprintf(buf + len, buf_len - len, "dyn_cca_status = %u\n", + le32_to_cpu(htt_stats_buf->dyn_cca_status)); + len += scnprintf(buf + len, buf_len - len, "rxdesense_thresh_hw = 0x%x\n", + le32_to_cpu(htt_stats_buf->rxdesense_thresh_hw)); + len += scnprintf(buf + len, buf_len - len, "rxdesense_thresh_sw = 0x%x\n\n", + le32_to_cpu(htt_stats_buf->rxdesense_thresh_sw)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_phy_reset_counters_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_phy_reset_counters_tlv *htt_stats_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PHY_RESET_COUNTERS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", + le32_to_cpu(htt_stats_buf->pdev_id)); + len += scnprintf(buf + len, buf_len - len, "cf_active_low_fail_cnt = %u\n", + le32_to_cpu(htt_stats_buf->cf_active_low_fail_cnt)); + len += scnprintf(buf + len, buf_len - len, "cf_active_low_pass_cnt = %u\n", + le32_to_cpu(htt_stats_buf->cf_active_low_pass_cnt)); + len += scnprintf(buf + len, buf_len - len, "phy_off_through_vreg_cnt = %u\n", + le32_to_cpu(htt_stats_buf->phy_off_through_vreg_cnt)); + len += scnprintf(buf + len, buf_len - len, "force_calibration_cnt = %u\n", + le32_to_cpu(htt_stats_buf->force_calibration_cnt)); + len += scnprintf(buf + len, buf_len - len, "rf_mode_switch_phy_off_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rf_mode_switch_phy_off_cnt)); + len += scnprintf(buf + len, buf_len - len, "temperature_recal_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->temperature_recal_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_phy_tpc_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_phy_tpc_stats_tlv *htt_stats_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_PHY_TPC_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", + le32_to_cpu(htt_stats_buf->pdev_id)); + len += scnprintf(buf + len, buf_len - len, "tx_power_scale = %u\n", + le32_to_cpu(htt_stats_buf->tx_power_scale)); + len += scnprintf(buf + len, buf_len - len, "tx_power_scale_db = %u\n", + le32_to_cpu(htt_stats_buf->tx_power_scale_db)); + len += scnprintf(buf + len, buf_len - len, "min_negative_tx_power = %d\n", + le32_to_cpu(htt_stats_buf->min_negative_tx_power)); + len += scnprintf(buf + len, buf_len - len, "reg_ctl_domain = %u\n", + le32_to_cpu(htt_stats_buf->reg_ctl_domain)); + len += scnprintf(buf + len, buf_len - len, "twice_max_rd_power = %u\n", + le32_to_cpu(htt_stats_buf->twice_max_rd_power)); + len += scnprintf(buf + len, buf_len - len, "max_tx_power = %u\n", + le32_to_cpu(htt_stats_buf->max_tx_power)); + len += scnprintf(buf + len, buf_len - len, "home_max_tx_power = %u\n", + le32_to_cpu(htt_stats_buf->home_max_tx_power)); + len += scnprintf(buf + len, buf_len - len, "psd_power = %d\n", + le32_to_cpu(htt_stats_buf->psd_power)); + len += scnprintf(buf + len, buf_len - len, "eirp_power = %u\n", + le32_to_cpu(htt_stats_buf->eirp_power)); + len += scnprintf(buf + len, buf_len - len, "power_type_6ghz = %u\n", + le32_to_cpu(htt_stats_buf->power_type_6ghz)); + len += print_array_to_buf(buf, len, "max_reg_allowed_power", + htt_stats_buf->max_reg_allowed_power, + ATH12K_HTT_STATS_MAX_CHAINS, "\n"); + len += print_array_to_buf(buf, len, "max_reg_allowed_power_6ghz", + htt_stats_buf->max_reg_allowed_power_6ghz, + ATH12K_HTT_STATS_MAX_CHAINS, "\n"); + len += print_array_to_buf(buf, len, "sub_band_cfreq", + htt_stats_buf->sub_band_cfreq, + ATH12K_HTT_MAX_CH_PWR_INFO_SIZE, "\n"); + len += print_array_to_buf(buf, len, "sub_band_txpower", + htt_stats_buf->sub_band_txpower, + ATH12K_HTT_MAX_CH_PWR_INFO_SIZE, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_soc_txrx_stats_common_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_t2h_soc_txrx_stats_common_tlv *htt_stats_buf = tag_buf; + u64 drop_count; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + drop_count = ath12k_le32hilo_to_u64(htt_stats_buf->inv_peers_msdu_drop_count_hi, + htt_stats_buf->inv_peers_msdu_drop_count_lo); + + len += scnprintf(buf + len, buf_len - len, "HTT_SOC_COMMON_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "soc_drop_count = %llu\n\n", + drop_count); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_per_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_per_rate_stats_tlv *stats_buf = tag_buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 ru_size_cnt = 0; + u32 rc_mode, ru_type; + u8 *buf = stats_req->buf, i; + const char *mode_prefix; + + if (tag_len < sizeof(*stats_buf)) + return; + + rc_mode = le32_to_cpu(stats_buf->rc_mode); + ru_type = le32_to_cpu(stats_buf->ru_type); + + switch (rc_mode) { + case ATH12K_HTT_STATS_RC_MODE_DLSU: + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PER_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "\nPER_STATS_SU:\n"); + mode_prefix = "su"; + break; + case ATH12K_HTT_STATS_RC_MODE_DLMUMIMO: + len += scnprintf(buf + len, buf_len - len, "\nPER_STATS_DL_MUMIMO:\n"); + mode_prefix = "mu"; + break; + case ATH12K_HTT_STATS_RC_MODE_DLOFDMA: + len += scnprintf(buf + len, buf_len - len, "\nPER_STATS_DL_OFDMA:\n"); + mode_prefix = "ofdma"; + if (ru_type == ATH12K_HTT_STATS_RU_TYPE_SINGLE_RU_ONLY) + ru_size_cnt = ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS; + else if (ru_type == ATH12K_HTT_STATS_RU_TYPE_SINGLE_AND_MULTI_RU) + ru_size_cnt = ATH12K_HTT_TX_RX_PDEV_NUM_BE_RU_SIZE_CNTRS; + break; + case ATH12K_HTT_STATS_RC_MODE_ULMUMIMO: + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PER_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "\nPER_STATS_UL_MUMIMO:\n"); + mode_prefix = "ulmu"; + break; + case ATH12K_HTT_STATS_RC_MODE_ULOFDMA: + len += scnprintf(buf + len, buf_len - len, "\nPER_STATS_UL_OFDMA:\n"); + mode_prefix = "ulofdma"; + if (ru_type == ATH12K_HTT_STATS_RU_TYPE_SINGLE_RU_ONLY) + ru_size_cnt = ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS; + else if (ru_type == ATH12K_HTT_STATS_RU_TYPE_SINGLE_AND_MULTI_RU) + ru_size_cnt = ATH12K_HTT_TX_RX_PDEV_NUM_BE_RU_SIZE_CNTRS; + break; + default: + return; + } + + len += scnprintf(buf + len, buf_len - len, "\nPER per BW:\n"); + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULMUMIMO) + len += scnprintf(buf + len, buf_len - len, "data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_tried_%s = ", + mode_prefix); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_BW_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_bw[i].ppdus_tried)); + len += scnprintf(buf + len, buf_len - len, " %u:%u\n", i, + le32_to_cpu(stats_buf->per_bw320.ppdus_tried)); + + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULMUMIMO) + len += scnprintf(buf + len, buf_len - len, "non_data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_ack_failed_%s = ", + mode_prefix); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_BW_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_bw[i].ppdus_ack_failed)); + len += scnprintf(buf + len, buf_len - len, " %u:%u\n", i, + le32_to_cpu(stats_buf->per_bw320.ppdus_ack_failed)); + + len += scnprintf(buf + len, buf_len - len, "mpdus_tried_%s = ", mode_prefix); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_BW_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_bw[i].mpdus_tried)); + len += scnprintf(buf + len, buf_len - len, " %u:%u\n", i, + le32_to_cpu(stats_buf->per_bw320.mpdus_tried)); + + len += scnprintf(buf + len, buf_len - len, "mpdus_failed_%s = ", mode_prefix); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_BW_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u", i, + le32_to_cpu(stats_buf->per_bw[i].mpdus_failed)); + len += scnprintf(buf + len, buf_len - len, " %u:%u\n", i, + le32_to_cpu(stats_buf->per_bw320.mpdus_failed)); + + len += scnprintf(buf + len, buf_len - len, "\nPER per NSS:\n"); + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULMUMIMO) + len += scnprintf(buf + len, buf_len - len, "data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_tried_%s = ", + mode_prefix); + for (i = 0; i < ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i + 1, + le32_to_cpu(stats_buf->per_nss[i].ppdus_tried)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULMUMIMO) + len += scnprintf(buf + len, buf_len - len, "non_data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_ack_failed_%s = ", + mode_prefix); + for (i = 0; i < ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i + 1, + le32_to_cpu(stats_buf->per_nss[i].ppdus_ack_failed)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "mpdus_tried_%s = ", mode_prefix); + for (i = 0; i < ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i + 1, + le32_to_cpu(stats_buf->per_nss[i].mpdus_tried)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "mpdus_failed_%s = ", mode_prefix); + for (i = 0; i < ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i + 1, + le32_to_cpu(stats_buf->per_nss[i].mpdus_failed)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "\nPER per MCS:\n"); + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULMUMIMO) + len += scnprintf(buf + len, buf_len - len, "data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_tried_%s = ", + mode_prefix); + for (i = 0; i < ATH12K_HTT_TXBF_RATE_STAT_NUM_MCS_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_mcs[i].ppdus_tried)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULMUMIMO) + len += scnprintf(buf + len, buf_len - len, "non_data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_ack_failed_%s = ", + mode_prefix); + for (i = 0; i < ATH12K_HTT_TXBF_RATE_STAT_NUM_MCS_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_mcs[i].ppdus_ack_failed)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "mpdus_tried_%s = ", mode_prefix); + for (i = 0; i < ATH12K_HTT_TXBF_RATE_STAT_NUM_MCS_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_mcs[i].mpdus_tried)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "mpdus_failed_%s = ", mode_prefix); + for (i = 0; i < ATH12K_HTT_TXBF_RATE_STAT_NUM_MCS_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %u:%u ", i, + le32_to_cpu(stats_buf->per_mcs[i].mpdus_failed)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + if ((rc_mode == ATH12K_HTT_STATS_RC_MODE_DLOFDMA || + rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA) && + ru_type != ATH12K_HTT_STATS_RU_TYPE_INVALID) { + len += scnprintf(buf + len, buf_len - len, "\nPER per RU:\n"); + + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA) + len += scnprintf(buf + len, buf_len - len, "data_ppdus_%s = ", + mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, "ppdus_tried_%s = ", + mode_prefix); + for (i = 0; i < ru_size_cnt; i++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_tx_ru_size_to_str(ru_type, i), + le32_to_cpu(stats_buf->ru[i].ppdus_tried)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_ULOFDMA) + len += scnprintf(buf + len, buf_len - len, + "non_data_ppdus_%s = ", mode_prefix); + else + len += scnprintf(buf + len, buf_len - len, + "ppdus_ack_failed_%s = ", mode_prefix); + for (i = 0; i < ru_size_cnt; i++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_tx_ru_size_to_str(ru_type, i), + le32_to_cpu(stats_buf->ru[i].ppdus_ack_failed)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "mpdus_tried_%s = ", + mode_prefix); + for (i = 0; i < ru_size_cnt; i++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_tx_ru_size_to_str(ru_type, i), + le32_to_cpu(stats_buf->ru[i].mpdus_tried)); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "mpdus_failed_%s = ", + mode_prefix); + for (i = 0; i < ru_size_cnt; i++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_tx_ru_size_to_str(ru_type, i), + le32_to_cpu(stats_buf->ru[i].mpdus_failed)); + len += scnprintf(buf + len, buf_len - len, "\n\n"); + } + + if (rc_mode == ATH12K_HTT_STATS_RC_MODE_DLMUMIMO) { + len += scnprintf(buf + len, buf_len - len, "\nlast_probed_bw = %u\n", + le32_to_cpu(stats_buf->last_probed_bw)); + len += scnprintf(buf + len, buf_len - len, "last_probed_nss = %u\n", + le32_to_cpu(stats_buf->last_probed_nss)); + len += scnprintf(buf + len, buf_len - len, "last_probed_mcs = %u\n", + le32_to_cpu(stats_buf->last_probed_mcs)); + len += print_array_to_buf(buf, len, "MU Probe count per RC MODE", + stats_buf->probe_cnt, + ATH12K_HTT_RC_MODE_2D_COUNT, "\n\n"); + } + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ast_entry_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_ast_entry_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_addr_l32; + u32 mac_addr_h16; + u32 ast_info; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_addr_l32 = le32_to_cpu(htt_stats_buf->mac_addr.mac_addr_l32); + mac_addr_h16 = le32_to_cpu(htt_stats_buf->mac_addr.mac_addr_h16); + ast_info = le32_to_cpu(htt_stats_buf->info); + + len += scnprintf(buf + len, buf_len - len, "HTT_AST_ENTRY_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "ast_index = %u\n", + le32_to_cpu(htt_stats_buf->ast_index)); + len += scnprintf(buf + len, buf_len - len, + "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + u32_get_bits(mac_addr_l32, ATH12K_HTT_MAC_ADDR_L32_0), + u32_get_bits(mac_addr_l32, ATH12K_HTT_MAC_ADDR_L32_1), + u32_get_bits(mac_addr_l32, ATH12K_HTT_MAC_ADDR_L32_2), + u32_get_bits(mac_addr_l32, ATH12K_HTT_MAC_ADDR_L32_3), + u32_get_bits(mac_addr_h16, ATH12K_HTT_MAC_ADDR_H16_0), + u32_get_bits(mac_addr_h16, ATH12K_HTT_MAC_ADDR_H16_1)); + + len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", + le32_to_cpu(htt_stats_buf->sw_peer_id)); + len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_PDEV_ID_INFO)); + len += scnprintf(buf + len, buf_len - len, "vdev_id = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_VDEV_ID_INFO)); + len += scnprintf(buf + len, buf_len - len, "next_hop = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_NEXT_HOP_INFO)); + len += scnprintf(buf + len, buf_len - len, "mcast = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_MCAST_INFO)); + len += scnprintf(buf + len, buf_len - len, "monitor_direct = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_MONITOR_DIRECT_INFO)); + len += scnprintf(buf + len, buf_len - len, "mesh_sta = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_MESH_STA_INFO)); + len += scnprintf(buf + len, buf_len - len, "mec = %u\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_MEC_INFO)); + len += scnprintf(buf + len, buf_len - len, "intra_bss = %u\n\n", + u32_get_bits(ast_info, ATH12K_HTT_AST_INTRA_BSS_INFO)); + + stats_req->buf_len = len; +} + +static const char* +ath12k_htt_get_punct_dir_type_str(enum ath12k_htt_stats_direction direction) +{ + switch (direction) { + case ATH12K_HTT_STATS_DIRECTION_TX: + return "tx"; + case ATH12K_HTT_STATS_DIRECTION_RX: + return "rx"; + default: + return "unknown"; + } +} + +static const char* +ath12k_htt_get_punct_ppdu_type_str(enum ath12k_htt_stats_ppdu_type ppdu_type) +{ + switch (ppdu_type) { + case ATH12K_HTT_STATS_PPDU_TYPE_MODE_SU: + return "su"; + case ATH12K_HTT_STATS_PPDU_TYPE_DL_MU_MIMO: + return "dl_mu_mimo"; + case ATH12K_HTT_STATS_PPDU_TYPE_UL_MU_MIMO: + return "ul_mu_mimo"; + case ATH12K_HTT_STATS_PPDU_TYPE_DL_MU_OFDMA: + return "dl_mu_ofdma"; + case ATH12K_HTT_STATS_PPDU_TYPE_UL_MU_OFDMA: + return "ul_mu_ofdma"; + default: + return "unknown"; + } +} + +static const char* +ath12k_htt_get_punct_pream_type_str(enum ath12k_htt_stats_param_type pream_type) +{ + switch (pream_type) { + case ATH12K_HTT_STATS_PREAM_OFDM: + return "ofdm"; + case ATH12K_HTT_STATS_PREAM_CCK: + return "cck"; + case ATH12K_HTT_STATS_PREAM_HT: + return "ht"; + case ATH12K_HTT_STATS_PREAM_VHT: + return "ac"; + case ATH12K_HTT_STATS_PREAM_HE: + return "ax"; + case ATH12K_HTT_STATS_PREAM_EHT: + return "be"; + default: + return "unknown"; + } +} + +static void +ath12k_htt_print_puncture_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_puncture_stats_tlv *stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + const char *direction; + const char *ppdu_type; + const char *preamble; + u32 mac_id__word; + u32 subband_limit; + u8 i; + + if (tag_len < sizeof(*stats_buf)) + return; + + mac_id__word = le32_to_cpu(stats_buf->mac_id__word); + subband_limit = min(le32_to_cpu(stats_buf->subband_cnt), + ATH12K_HTT_PUNCT_STATS_MAX_SUBBAND_CNT); + + direction = ath12k_htt_get_punct_dir_type_str(le32_to_cpu(stats_buf->direction)); + ppdu_type = ath12k_htt_get_punct_ppdu_type_str(le32_to_cpu(stats_buf->ppdu_type)); + preamble = ath12k_htt_get_punct_pream_type_str(le32_to_cpu(stats_buf->preamble)); + + len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_PUNCTURE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id__word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, + "%s_%s_%s_last_used_pattern_mask = 0x%08x\n", + direction, preamble, ppdu_type, + le32_to_cpu(stats_buf->last_used_pattern_mask)); + + for (i = 0; i < subband_limit; i++) { + len += scnprintf(buf + len, buf_len - len, + "%s_%s_%s_num_subbands_used_cnt_%02d = %u\n", + direction, preamble, ppdu_type, i + 1, + le32_to_cpu(stats_buf->num_subbands_used_cnt[i])); + } + len += scnprintf(buf + len, buf_len - len, "\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_dmac_reset_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_dmac_reset_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u64 time; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_DMAC_RESET_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "reset_count = %u\n", + le32_to_cpu(htt_stats_buf->reset_count)); + time = ath12k_le32hilo_to_u64(htt_stats_buf->reset_time_hi_ms, + htt_stats_buf->reset_time_lo_ms); + len += scnprintf(buf + len, buf_len - len, "reset_time_ms = %llu\n", time); + time = ath12k_le32hilo_to_u64(htt_stats_buf->disengage_time_hi_ms, + htt_stats_buf->disengage_time_lo_ms); + len += scnprintf(buf + len, buf_len - len, "disengage_time_ms = %llu\n", time); + + time = ath12k_le32hilo_to_u64(htt_stats_buf->engage_time_hi_ms, + htt_stats_buf->engage_time_lo_ms); + len += scnprintf(buf + len, buf_len - len, "engage_time_ms = %llu\n", time); + + len += scnprintf(buf + len, buf_len - len, "disengage_count = %u\n", + le32_to_cpu(htt_stats_buf->disengage_count)); + len += scnprintf(buf + len, buf_len - len, "engage_count = %u\n", + le32_to_cpu(htt_stats_buf->engage_count)); + len += scnprintf(buf + len, buf_len - len, "drain_dest_ring_mask = 0x%x\n\n", + le32_to_cpu(htt_stats_buf->drain_dest_ring_mask)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_pdev_sched_algo_ofdma_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_sched_algo_ofdma_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_SCHED_ALGO_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += print_array_to_buf(buf, len, "rate_based_dlofdma_enabled_count", + htt_stats_buf->rate_based_dlofdma_enabled_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "rate_based_dlofdma_disabled_count", + htt_stats_buf->rate_based_dlofdma_disabled_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "rate_based_dlofdma_probing_count", + htt_stats_buf->rate_based_dlofdma_disabled_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "rate_based_dlofdma_monitoring_count", + htt_stats_buf->rate_based_dlofdma_monitor_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "chan_acc_lat_based_dlofdma_enabled_count", + htt_stats_buf->chan_acc_lat_based_dlofdma_enabled_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "chan_acc_lat_based_dlofdma_disabled_count", + htt_stats_buf->chan_acc_lat_based_dlofdma_disabled_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "chan_acc_lat_based_dlofdma_monitoring_count", + htt_stats_buf->chan_acc_lat_based_dlofdma_monitor_cnt, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "downgrade_to_dl_su_ru_alloc_fail", + htt_stats_buf->downgrade_to_dl_su_ru_alloc_fail, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "candidate_list_single_user_disable_ofdma", + htt_stats_buf->candidate_list_single_user_disable_ofdma, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "dl_cand_list_dropped_high_ul_qos_weight", + htt_stats_buf->dl_cand_list_dropped_high_ul_qos_weight, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "ax_dlofdma_disabled_due_to_pipelining", + htt_stats_buf->ax_dlofdma_disabled_due_to_pipelining, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "dlofdma_disabled_su_only_eligible", + htt_stats_buf->dlofdma_disabled_su_only_eligible, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "dlofdma_disabled_consec_no_mpdus_tried", + htt_stats_buf->dlofdma_disabled_consec_no_mpdus_tried, + ATH12K_HTT_NUM_AC_WMM, "\n"); + len += print_array_to_buf(buf, len, "dlofdma_disabled_consec_no_mpdus_success", + htt_stats_buf->dlofdma_disabled_consec_no_mpdus_success, + ATH12K_HTT_NUM_AC_WMM, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_pdev_rate_stats_be_ofdma_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_rate_stats_be_ofdma_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + u8 i; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_RATE_STATS_BE_OFDMA_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "be_ofdma_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->be_ofdma_tx_ldpc)); + len += print_array_to_buf(buf, len, "be_ofdma_tx_mcs", + htt_stats_buf->be_ofdma_tx_mcs, + ATH12K_HTT_TX_PDEV_NUM_BE_MCS_CNTRS, "\n"); + len += print_array_to_buf(buf, len, "be_ofdma_eht_sig_mcs", + htt_stats_buf->be_ofdma_eht_sig_mcs, + ATH12K_HTT_TX_PDEV_NUM_EHT_SIG_MCS_CNTRS, "\n"); + len += scnprintf(buf + len, buf_len - len, "be_ofdma_tx_ru_size = "); + for (i = 0; i < ATH12K_HTT_TX_RX_PDEV_NUM_BE_RU_SIZE_CNTRS; i++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_be_tx_rx_ru_size_to_str(i), + le32_to_cpu(htt_stats_buf->be_ofdma_tx_ru_size[i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + len += print_array_to_buf_index(buf, len, "be_ofdma_tx_nss = ", 1, + htt_stats_buf->be_ofdma_tx_nss, + ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS, + "\n"); + len += print_array_to_buf(buf, len, "be_ofdma_tx_bw", + htt_stats_buf->be_ofdma_tx_bw, + ATH12K_HTT_TX_PDEV_NUM_BE_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_TX_PDEV_NUM_GI_CNTRS; i++) { + len += scnprintf(buf + len, buf_len - len, + "be_ofdma_tx_gi[%u]", i); + len += print_array_to_buf(buf, len, "", htt_stats_buf->gi[i], + ATH12K_HTT_TX_PDEV_NUM_BE_MCS_CNTRS, "\n"); + } + len += scnprintf(buf + len, buf_len - len, "\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_mbssid_ctrl_frame_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_MBSSID_CTRL_FRAME_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "basic_trigger_across_bss = %u\n", + le32_to_cpu(htt_stats_buf->basic_trigger_across_bss)); + len += scnprintf(buf + len, buf_len - len, "basic_trigger_within_bss = %u\n", + le32_to_cpu(htt_stats_buf->basic_trigger_within_bss)); + len += scnprintf(buf + len, buf_len - len, "bsr_trigger_across_bss = %u\n", + le32_to_cpu(htt_stats_buf->bsr_trigger_across_bss)); + len += scnprintf(buf + len, buf_len - len, "bsr_trigger_within_bss = %u\n", + le32_to_cpu(htt_stats_buf->bsr_trigger_within_bss)); + len += scnprintf(buf + len, buf_len - len, "mu_rts_across_bss = %u\n", + le32_to_cpu(htt_stats_buf->mu_rts_across_bss)); + len += scnprintf(buf + len, buf_len - len, "mu_rts_within_bss = %u\n", + le32_to_cpu(htt_stats_buf->mu_rts_within_bss)); + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_trigger_across_bss = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_trigger_across_bss)); + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_trigger_within_bss = %u\n\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_trigger_within_bss)); + + stats_req->buf_len = len; +} + +static inline void +ath12k_htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i, j; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ofdma_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ofdma_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rts_cnt)); + len += scnprintf(buf + len, buf_len - len, "rts_success = %u\n", + le32_to_cpu(htt_stats_buf->rts_success)); + len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n", + le32_to_cpu(htt_stats_buf->ack_rssi)); + len += scnprintf(buf + len, buf_len - len, + "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 12 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[0]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[2]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[3])); + len += scnprintf(buf + len, buf_len - len, + "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" + " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[0]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[2]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[3]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[4]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[5]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[6]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[7])); + len += scnprintf(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u\n", + le32_to_cpu(htt_stats_buf->tx_he_ltf[1]), + le32_to_cpu(htt_stats_buf->tx_he_ltf[2]), + le32_to_cpu(htt_stats_buf->tx_he_ltf[3])); + + len += print_array_to_buf(buf, len, "tx_mcs", htt_stats_buf->tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_mcs_ext[j])); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS + + ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_mcs_ext_2[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_mcs", + htt_stats_buf->ax_mu_mimo_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "ofdma_tx_mcs", + htt_stats_buf->ofdma_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ofdma_tx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ofdma_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ofdma_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "tx_bw", htt_stats_buf->tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, NULL); + len += scnprintf(buf + len, buf_len - len, ", %u:%u\n", + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_bw_320mhz)); + + len += print_array_to_buf(buf, len, "tx_stbc", + htt_stats_buf->tx_stbc, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_stbc_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ac_mu_mimo_tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ac_mu_mimo_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ax_mu_mimo_tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ax_mimo_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ax_tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ofdma_tx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ofdma_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ofd_tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + len += print_array_to_buf(buf, len, "tx_su_mcs", htt_stats_buf->tx_su_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_mu_mcs", htt_stats_buf->tx_mu_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_mcs", + htt_stats_buf->ac_mu_mimo_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_bw", + htt_stats_buf->ac_mu_mimo_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_bw", + htt_stats_buf->ax_mu_mimo_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ofdma_tx_bw", + htt_stats_buf->ofdma_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_pream", htt_stats_buf->tx_pream, + ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + len += print_array_to_buf(buf, len, "tx_dcm", htt_stats_buf->tx_dcm, + ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); + + stats_req->buf_len = len; +} + +static inline void +ath12k_htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i, j; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word); + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", + le32_to_cpu(htt_stats_buf->nsts)); + len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->rx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rts_cnt)); + len += scnprintf(buf + len, buf_len - len, "rssi_mgmt = %u\n", + le32_to_cpu(htt_stats_buf->rssi_mgmt)); + len += scnprintf(buf + len, buf_len - len, "rssi_data = %u\n", + le32_to_cpu(htt_stats_buf->rssi_data)); + len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n", + le32_to_cpu(htt_stats_buf->rssi_comb)); + len += scnprintf(buf + len, buf_len - len, "rssi_in_dbm = %d\n", + le32_to_cpu(htt_stats_buf->rssi_in_dbm)); + len += scnprintf(buf + len, buf_len - len, "rx_evm_nss_count = %u\n", + le32_to_cpu(htt_stats_buf->nss_count)); + len += scnprintf(buf + len, buf_len - len, "rx_evm_pilot_count = %u\n", + le32_to_cpu(htt_stats_buf->pilot_count)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_su_ext = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_su_ext)); + len += scnprintf(buf + len, buf_len - len, "rx_11ac_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ac_mumimo)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_mumimo)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ofdma)); + len += scnprintf(buf + len, buf_len - len, "txbf = %u\n", + le32_to_cpu(htt_stats_buf->txbf)); + len += scnprintf(buf + len, buf_len - len, "rx_su_ndpa = %u\n", + le32_to_cpu(htt_stats_buf->rx_su_ndpa)); + len += scnprintf(buf + len, buf_len - len, "rx_mu_ndpa = %u\n", + le32_to_cpu(htt_stats_buf->rx_mu_ndpa)); + len += scnprintf(buf + len, buf_len - len, "rx_br_poll = %u\n", + le32_to_cpu(htt_stats_buf->rx_br_poll)); + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_low = %u\n", + le32_to_cpu(htt_stats_buf->rx_active_dur_us_low)); + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n", + le32_to_cpu(htt_stats_buf->rx_active_dur_us_high)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n", + le32_to_cpu(htt_stats_buf->per_chain_rssi_pkt_type)); + + len += print_array_to_buf(buf, len, "rx_nss", htt_stats_buf->rx_nss, + ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "rx_dcm", htt_stats_buf->rx_dcm, + ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_stbc", htt_stats_buf->rx_stbc, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_bw", htt_stats_buf->rx_bw, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_pream", htt_stats_buf->rx_pream, + ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs", + htt_stats_buf->rx_11ax_su_txbf_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs", + htt_stats_buf->rx_11ax_mu_txbf_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_legacy_cck_rate", + htt_stats_buf->rx_legacy_cck_rate, + ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS, "\n"); + len += print_array_to_buf(buf, len, "rx_legacy_ofdm_rate", + htt_stats_buf->rx_legacy_ofdm_rate, + ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs", + htt_stats_buf->ul_ofdma_rx_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_nss", + htt_stats_buf->ul_ofdma_rx_nss, + ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw", + htt_stats_buf->ul_ofdma_rx_bw, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_ppdu", + htt_stats_buf->rx_ulofdma_non_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_data_ppdu", + htt_stats_buf->rx_ulofdma_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_ok", + htt_stats_buf->rx_ulofdma_mpdu_ok, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_fail", + htt_stats_buf->rx_ulofdma_mpdu_fail, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_nusers", + htt_stats_buf->rx_ulofdma_non_data_nusers, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_data_nusers", + htt_stats_buf->rx_ulofdma_data_nusers, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs", + htt_stats_buf->rx_11ax_dl_ofdma_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_ru", + htt_stats_buf->rx_11ax_dl_ofdma_ru, + ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_non_data_ppdu", + htt_stats_buf->rx_ulmumimo_non_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_data_ppdu", + htt_stats_buf->rx_ulmumimo_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_ok", + htt_stats_buf->rx_ulmumimo_mpdu_ok, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_fail", + htt_stats_buf->rx_ulmumimo_mpdu_fail, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + + len += print_array_to_buf(buf, len, "rx_mcs", + htt_stats_buf->rx_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->rx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "pilot_evm_db[%u] =", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_pil_evm_db[j], + ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS, + "\n"); + } + + len += scnprintf(buf + len, buf_len - len, "pilot_evm_db_mean ="); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", i, + le32_to_cpu(htt_stats_buf->rx_pilot_evm_db_mean[i])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rssi_chain_in_db[%u] = ", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u: %d,", i, + htt_stats_buf->rssi_chain_in_db[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_gi[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_rx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ul_ofdma_rx_gi[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_fd_rssi: nss[%u] = ", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", + i, htt_stats_buf->rx_ul_fd_rssi[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_per_chain_rssi_in_dbm[%u] =", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", + i, + htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + stats_req->buf_len = len; +} + +static inline void +ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_rate_ext_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_EXT_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rssi_mgmt_in_dbm = %d\n", + le32_to_cpu(htt_stats_buf->rssi_mgmt_in_dbm)); + + len += print_array_to_buf(buf, len, "rx_stbc_ext", + htt_stats_buf->rx_stbc_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs_ext", + htt_stats_buf->ul_ofdma_rx_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs_ext", + htt_stats_buf->rx_11ax_su_txbf_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs_ext", + htt_stats_buf->rx_11ax_mu_txbf_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs_ext", + htt_stats_buf->rx_11ax_dl_ofdma_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_bw_ext", + htt_stats_buf->rx_bw_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_su_punctured_mode", + htt_stats_buf->rx_su_punctured_mode, + ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS, + "\n"); + + len += print_array_to_buf(buf, len, "rx_mcs_ext", + htt_stats_buf->rx_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + NULL); + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + le32_to_cpu(htt_stats_buf->rx_mcs_ext_2[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_gi_ext[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_gi_ext[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_rx_gi_ext[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ul_ofdma_rx_gi_ext[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + "\n"); + } + + stats_req->buf_len = len; +} + +static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, + u16 tag, u16 len, const void *tag_buf, + void *user_data) +{ + struct debug_htt_stats_req *stats_req = user_data; + + switch (tag) { + case HTT_STATS_TX_PDEV_CMN_TAG: + htt_print_tx_pdev_stats_cmn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_UNDERRUN_TAG: + htt_print_tx_pdev_stats_urrn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_TAG: + htt_print_tx_pdev_stats_sifs_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_FLUSH_TAG: + htt_print_tx_pdev_stats_flush_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_HIST_TAG: + htt_print_tx_pdev_stats_sifs_hist_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG: + htt_print_pdev_ctrl_path_tx_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_MU_PPDU_DIST_TAG: + htt_print_tx_pdev_mu_ppdu_dist_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SCHED_CMN_TAG: + ath12k_htt_print_stats_tx_sched_cmn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG: + ath12k_htt_print_tx_pdev_stats_sched_per_txq_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG: + ath12k_htt_print_sched_txq_cmd_posted_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG: + ath12k_htt_print_sched_txq_cmd_reaped_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG: + ath12k_htt_print_sched_txq_sched_order_su_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG: + ath12k_htt_print_sched_txq_sched_ineligibility_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG: + ath12k_htt_print_sched_txq_supercycle_trigger_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_HW_PDEV_ERRS_TAG: + ath12k_htt_print_hw_stats_pdev_errs_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_HW_INTR_MISC_TAG: + ath12k_htt_print_hw_stats_intr_misc_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_WHAL_TX_TAG: + ath12k_htt_print_hw_stats_whal_tx_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_HW_WAR_TAG: + ath12k_htt_print_hw_war_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_CMN_TAG: + ath12k_htt_print_tx_tqm_cmn_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_ERROR_STATS_TAG: + ath12k_htt_print_tx_tqm_error_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_GEN_MPDU_TAG: + ath12k_htt_print_tx_tqm_gen_mpdu_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_LIST_MPDU_TAG: + ath12k_htt_print_tx_tqm_list_mpdu_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG: + ath12k_htt_print_tx_tqm_list_mpdu_cnt_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_PDEV_TAG: + ath12k_htt_print_tx_tqm_pdev_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_CMN_TAG: + ath12k_htt_print_tx_de_cmn_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_EAPOL_PACKETS_TAG: + ath12k_htt_print_tx_de_eapol_packets_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_CLASSIFY_STATS_TAG: + ath12k_htt_print_tx_de_classify_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG: + ath12k_htt_print_tx_de_classify_failed_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG: + ath12k_htt_print_tx_de_classify_status_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG: + ath12k_htt_print_tx_de_enqueue_packets_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG: + ath12k_htt_print_tx_de_enqueue_discard_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_DE_COMPL_STATS_TAG: + ath12k_htt_print_tx_de_compl_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_CMN_STATS_TAG: + ath12k_htt_print_tx_selfgen_cmn_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_AC_STATS_TAG: + ath12k_htt_print_tx_selfgen_ac_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_AX_STATS_TAG: + ath12k_htt_print_tx_selfgen_ax_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_BE_STATS_TAG: + ath12k_htt_print_tx_selfgen_be_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG: + ath12k_htt_print_tx_selfgen_ac_err_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG: + ath12k_htt_print_tx_selfgen_ax_err_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_BE_ERR_STATS_TAG: + ath12k_htt_print_tx_selfgen_be_err_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SELFGEN_AC_SCHED_STATUS_STATS_TAG: + ath12k_htt_print_tx_selfgen_ac_sched_status_stats_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_TX_SELFGEN_AX_SCHED_STATUS_STATS_TAG: + ath12k_htt_print_tx_selfgen_ax_sched_status_stats_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_TX_SELFGEN_BE_SCHED_STATUS_STATS_TAG: + ath12k_htt_print_tx_selfgen_be_sched_status_stats_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_STRING_TAG: + ath12k_htt_print_stats_string_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SRING_STATS_TAG: + ath12k_htt_print_sring_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SFM_CMN_TAG: + ath12k_htt_print_sfm_cmn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SFM_CLIENT_TAG: + ath12k_htt_print_sfm_client_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SFM_CLIENT_USER_TAG: + ath12k_htt_print_sfm_client_user_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG: + ath12k_htt_print_tx_pdev_mu_mimo_sch_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_MUMIMO_GRP_STATS_TAG: + ath12k_htt_print_tx_pdev_mumimo_grp_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_MPDU_STATS_TAG: + ath12k_htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_CCA_1SEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG: + ath12k_htt_print_pdev_cca_stats_hist_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_CCA_COUNTERS_TAG: + ath12k_htt_print_pdev_stats_cca_counters_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_SOUNDING_STATS_TAG: + ath12k_htt_print_tx_sounding_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_OBSS_PD_TAG: + ath12k_htt_print_pdev_obss_pd_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_CTX_TAG: + ath12k_htt_print_latency_prof_ctx_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_CNT_TAG: + ath12k_htt_print_latency_prof_cnt(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_PROF_STATS_TAG: + ath12k_htt_print_latency_prof_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG: + ath12k_htt_print_ul_ofdma_trigger_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG: + ath12k_htt_print_ul_ofdma_user_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG: + ath12k_htt_print_ul_mumimo_trig_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_FSE_STATS_TAG: + ath12k_htt_print_rx_fse_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: + ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TXBF_OFDMA_AX_NDPA_STATS_TAG: + ath12k_htt_print_txbf_ofdma_ax_ndpa_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TXBF_OFDMA_AX_NDP_STATS_TAG: + ath12k_htt_print_txbf_ofdma_ax_ndp_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TXBF_OFDMA_AX_BRP_STATS_TAG: + ath12k_htt_print_txbf_ofdma_ax_brp_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TXBF_OFDMA_AX_STEER_STATS_TAG: + ath12k_htt_print_txbf_ofdma_ax_steer_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TXBF_OFDMA_AX_STEER_MPDU_STATS_TAG: + ath12k_htt_print_txbf_ofdma_ax_steer_mpdu_stats_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_DLPAGER_STATS_TAG: + ath12k_htt_print_dlpager_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PHY_STATS_TAG: + ath12k_htt_print_phy_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PHY_COUNTERS_TAG: + ath12k_htt_print_phy_counters_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PHY_RESET_STATS_TAG: + ath12k_htt_print_phy_reset_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PHY_RESET_COUNTERS_TAG: + ath12k_htt_print_phy_reset_counters_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PHY_TPC_STATS_TAG: + ath12k_htt_print_phy_tpc_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SOC_TXRX_STATS_COMMON_TAG: + ath12k_htt_print_soc_txrx_stats_common_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PER_RATE_STATS_TAG: + ath12k_htt_print_tx_per_rate_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_AST_ENTRY_TAG: + ath12k_htt_print_ast_entry_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_PUNCTURE_STATS_TAG: + ath12k_htt_print_puncture_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_DMAC_RESET_STATS_TAG: + ath12k_htt_print_dmac_reset_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_SCHED_ALGO_OFDMA_STATS_TAG: + ath12k_htt_print_pdev_sched_algo_ofdma_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_RATE_STATS_BE_OFDMA_TAG: + ath12k_htt_print_tx_pdev_rate_stats_be_ofdma_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_MBSSID_CTRL_FRAME_STATS_TAG: + ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_TX_PDEV_RATE_STATS_TAG: + ath12k_htt_print_tx_pdev_rate_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_RATE_STATS_TAG: + ath12k_htt_print_rx_pdev_rate_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG: + ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(tag_buf, len, stats_req); + break; + default: + break; + } + + return 0; +} + +void ath12k_debugfs_htt_ext_stats_handler(struct ath12k_base *ab, + struct sk_buff *skb) +{ + struct ath12k_htt_extd_stats_msg *msg; + struct debug_htt_stats_req *stats_req; + struct ath12k *ar; + u32 len, pdev_id, stats_info; + u64 cookie; + int ret; + bool send_completion = false; + + msg = (struct ath12k_htt_extd_stats_msg *)skb->data; + cookie = le64_to_cpu(msg->cookie); + + if (u64_get_bits(cookie, ATH12K_HTT_STATS_COOKIE_MSB) != + ATH12K_HTT_STATS_MAGIC_VALUE) { + ath12k_warn(ab, "received invalid htt ext stats event\n"); + return; + } + + pdev_id = u64_get_bits(cookie, ATH12K_HTT_STATS_COOKIE_LSB); + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ath12k_warn(ab, "failed to get ar for pdev_id %d\n", pdev_id); + goto exit; + } + + stats_req = ar->debug.htt_stats.stats_req; + if (!stats_req) + goto exit; + + spin_lock_bh(&ar->data_lock); + + stats_info = le32_to_cpu(msg->info1); + stats_req->done = u32_get_bits(stats_info, ATH12K_HTT_T2H_EXT_STATS_INFO1_DONE); + if (stats_req->done) + send_completion = true; + + spin_unlock_bh(&ar->data_lock); + + len = u32_get_bits(stats_info, ATH12K_HTT_T2H_EXT_STATS_INFO1_LENGTH); + if (len > skb->len) { + ath12k_warn(ab, "invalid length %d for HTT stats", len); + goto exit; + } + + ret = ath12k_dp_htt_tlv_iter(ab, msg->data, len, + ath12k_dbg_htt_ext_stats_parse, + stats_req); + if (ret) + ath12k_warn(ab, "Failed to parse tlv %d\n", ret); + + if (send_completion) + complete(&stats_req->htt_stats_rcvd); +exit: + rcu_read_unlock(); +} + +static ssize_t ath12k_read_htt_stats_type(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + enum ath12k_dbg_htt_ext_stats_type type; + char buf[32]; + size_t len; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + type = ar->debug.htt_stats.type; + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + len = scnprintf(buf, sizeof(buf), "%u\n", type); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath12k_write_htt_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + enum ath12k_dbg_htt_ext_stats_type type; + unsigned int cfg_param[4] = {0}; + const int size = 32; + int num_args; + + if (count > size) + return -EINVAL; + + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + num_args = sscanf(buf, "%u %u %u %u %u\n", &type, &cfg_param[0], + &cfg_param[1], &cfg_param[2], &cfg_param[3]); + if (!num_args || num_args > 5) + return -EINVAL; + + if (type == ATH12K_DBG_HTT_EXT_STATS_RESET || + type >= ATH12K_DBG_HTT_NUM_EXT_STATS) + return -EINVAL; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + + ar->debug.htt_stats.type = type; + ar->debug.htt_stats.cfg_param[0] = cfg_param[0]; + ar->debug.htt_stats.cfg_param[1] = cfg_param[1]; + ar->debug.htt_stats.cfg_param[2] = cfg_param[2]; + ar->debug.htt_stats.cfg_param[3] = cfg_param[3]; + + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return count; +} + +static const struct file_operations fops_htt_stats_type = { + .read = ath12k_read_htt_stats_type, + .write = ath12k_write_htt_stats_type, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath12k_debugfs_htt_stats_req(struct ath12k *ar) +{ + struct debug_htt_stats_req *stats_req = ar->debug.htt_stats.stats_req; + enum ath12k_dbg_htt_ext_stats_type type = stats_req->type; + u64 cookie; + int ret, pdev_id; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + init_completion(&stats_req->htt_stats_rcvd); + + pdev_id = ath12k_mac_get_target_pdev_id(ar); + stats_req->done = false; + stats_req->pdev_id = pdev_id; + + cookie = u64_encode_bits(ATH12K_HTT_STATS_MAGIC_VALUE, + ATH12K_HTT_STATS_COOKIE_MSB); + cookie |= u64_encode_bits(pdev_id, ATH12K_HTT_STATS_COOKIE_LSB); + + if (stats_req->override_cfg_param) { + cfg_params.cfg0 = stats_req->cfg_param[0]; + cfg_params.cfg1 = stats_req->cfg_param[1]; + cfg_params.cfg2 = stats_req->cfg_param[2]; + cfg_params.cfg3 = stats_req->cfg_param[3]; + } + + ret = ath12k_dp_tx_htt_h2t_ext_stats_req(ar, type, &cfg_params, cookie); + if (ret) { + ath12k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + return ret; + } + if (!wait_for_completion_timeout(&stats_req->htt_stats_rcvd, 3 * HZ)) { + spin_lock_bh(&ar->data_lock); + if (!stats_req->done) { + stats_req->done = true; + spin_unlock_bh(&ar->data_lock); + ath12k_warn(ar->ab, "stats request timed out\n"); + return -ETIMEDOUT; + } + spin_unlock_bh(&ar->data_lock); + } + + return 0; +} + +static int ath12k_open_htt_stats(struct inode *inode, + struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct debug_htt_stats_req *stats_req; + enum ath12k_dbg_htt_ext_stats_type type = ar->debug.htt_stats.type; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + if (type == ATH12K_DBG_HTT_EXT_STATS_RESET) + return -EPERM; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + + if (ah->state != ATH12K_HW_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + if (ar->debug.htt_stats.stats_req) { + ret = -EAGAIN; + goto err_unlock; + } + + stats_req = kzalloc(sizeof(*stats_req) + ATH12K_HTT_STATS_BUF_SIZE, GFP_KERNEL); + if (!stats_req) { + ret = -ENOMEM; + goto err_unlock; + } + + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = type; + stats_req->cfg_param[0] = ar->debug.htt_stats.cfg_param[0]; + stats_req->cfg_param[1] = ar->debug.htt_stats.cfg_param[1]; + stats_req->cfg_param[2] = ar->debug.htt_stats.cfg_param[2]; + stats_req->cfg_param[3] = ar->debug.htt_stats.cfg_param[3]; + stats_req->override_cfg_param = !!stats_req->cfg_param[0] || + !!stats_req->cfg_param[1] || + !!stats_req->cfg_param[2] || + !!stats_req->cfg_param[3]; + + ret = ath12k_debugfs_htt_stats_req(ar); + if (ret < 0) + goto out; + + file->private_data = stats_req; + + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return 0; +out: + kfree(stats_req); + ar->debug.htt_stats.stats_req = NULL; +err_unlock: + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return ret; +} + +static int ath12k_release_htt_stats(struct inode *inode, + struct file *file) +{ + struct ath12k *ar = inode->i_private; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + kfree(file->private_data); + ar->debug.htt_stats.stats_req = NULL; + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return 0; +} + +static ssize_t ath12k_read_htt_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH12K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_dump_htt_stats = { + .open = ath12k_open_htt_stats, + .release = ath12k_release_htt_stats, + .read = ath12k_read_htt_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_write_htt_stats_reset(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + enum ath12k_dbg_htt_ext_stats_type type; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + u8 param_pos; + int ret; + + ret = kstrtou32_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH12K_DBG_HTT_NUM_EXT_STATS || + type == ATH12K_DBG_HTT_EXT_STATS_RESET) + return -E2BIG; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + cfg_params.cfg0 = HTT_STAT_DEFAULT_RESET_START_OFFSET; + param_pos = (type >> 5) + 1; + + switch (param_pos) { + case ATH12K_HTT_STATS_RESET_PARAM_CFG_32_BYTES: + cfg_params.cfg1 = 1 << (cfg_params.cfg0 + type); + break; + case ATH12K_HTT_STATS_RESET_PARAM_CFG_64_BYTES: + cfg_params.cfg2 = ATH12K_HTT_STATS_RESET_BITMAP32_BIT(cfg_params.cfg0 + + type); + break; + case ATH12K_HTT_STATS_RESET_PARAM_CFG_128_BYTES: + cfg_params.cfg3 = ATH12K_HTT_STATS_RESET_BITMAP64_BIT(cfg_params.cfg0 + + type); + break; + default: + break; + } + + ret = ath12k_dp_tx_htt_h2t_ext_stats_req(ar, + ATH12K_DBG_HTT_EXT_STATS_RESET, + &cfg_params, + 0ULL); + if (ret) { + ath12k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + return ret; + } + + ar->debug.htt_stats.reset = type; + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return count; +} + +static const struct file_operations fops_htt_stats_reset = { + .write = ath12k_write_htt_stats_reset, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_htt_stats_register(struct ath12k *ar) +{ + debugfs_create_file("htt_stats_type", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_type); + debugfs_create_file("htt_stats", 0400, ar->debug.debugfs_pdev, + ar, &fops_dump_htt_stats); + debugfs_create_file("htt_stats_reset", 0200, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_reset); +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h new file mode 100644 index 000000000000..c2a02cf8a38b --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -0,0 +1,1875 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef DEBUG_HTT_STATS_H +#define DEBUG_HTT_STATS_H + +#define ATH12K_HTT_STATS_BUF_SIZE (1024 * 512) +#define ATH12K_HTT_STATS_COOKIE_LSB GENMASK_ULL(31, 0) +#define ATH12K_HTT_STATS_COOKIE_MSB GENMASK_ULL(63, 32) +#define ATH12K_HTT_STATS_MAGIC_VALUE 0xF0F0F0F0 +#define ATH12K_HTT_STATS_SUBTYPE_MAX 16 +#define ATH12K_HTT_MAX_STRING_LEN 256 + +#define ATH12K_HTT_STATS_RESET_BITMAP32_OFFSET(_idx) ((_idx) & 0x1f) +#define ATH12K_HTT_STATS_RESET_BITMAP64_OFFSET(_idx) ((_idx) & 0x3f) +#define ATH12K_HTT_STATS_RESET_BITMAP32_BIT(_idx) (1 << \ + ATH12K_HTT_STATS_RESET_BITMAP32_OFFSET(_idx)) +#define ATH12K_HTT_STATS_RESET_BITMAP64_BIT(_idx) (1 << \ + ATH12K_HTT_STATS_RESET_BITMAP64_OFFSET(_idx)) + +void ath12k_debugfs_htt_stats_register(struct ath12k *ar); + +#ifdef CONFIG_ATH12K_DEBUGFS +void ath12k_debugfs_htt_ext_stats_handler(struct ath12k_base *ab, + struct sk_buff *skb); +#else /* CONFIG_ATH12K_DEBUGFS */ +static inline void ath12k_debugfs_htt_ext_stats_handler(struct ath12k_base *ab, + struct sk_buff *skb) +{ +} +#endif + +/** + * DOC: target -> host extended statistics upload + * + * The following field definitions describe the format of the HTT + * target to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a single stats can span over multiple HTT stats indication + * due to the HTT message size limitation so every HTT ext stats + * indication will have tag-length-value stats information elements. + * The tag-length header for each HTT stats IND message also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A Done bit 1's indicate the end of the of stats info elements. + * + * + * |31 16|15 12|11|10 8|7 5|4 0| + * |--------------------------------------------------------------| + * | reserved | msg type | + * |--------------------------------------------------------------| + * | cookie LSBs | + * |--------------------------------------------------------------| + * | cookie MSBs | + * |--------------------------------------------------------------| + * | stats entry length | rsvd | D| S | stat type | + * |--------------------------------------------------------------| + * | type-specific stats info | + * | (see debugfs_htt_stats.h) | + * |--------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a extended statistics upload confirmation + * message. + * Value: 0x1c + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 7:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: ath12k_dbg_htt_ext_stats_type + * - STATUS + * Bits 10:8 + * Purpose: indicate whether the requested stats are present + * Value: + * 0 -> The requested stats have been delivered in full + * 1 -> The requested stats have been delivered in part + * 2 -> The requested stats could not be delivered (error case) + * 3 -> The requested stat type is either not recognized (invalid) + * - DONE + * Bits 11 + * Purpose: + * Indicates the completion of the stats entry, this will be the last + * stats conf HTT segment for the requested stats type. + * Value: + * 0 -> the stats retrieval is ongoing + * 1 -> the stats retrieval is complete + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. + */ + +#define ATH12K_HTT_T2H_EXT_STATS_INFO1_DONE BIT(11) +#define ATH12K_HTT_T2H_EXT_STATS_INFO1_LENGTH GENMASK(31, 16) + +struct ath12k_htt_extd_stats_msg { + __le32 info0; + __le64 cookie; + __le32 info1; + u8 data[]; +} __packed; + +/* htt_dbg_ext_stats_type */ +enum ath12k_dbg_htt_ext_stats_type { + ATH12K_DBG_HTT_EXT_STATS_RESET = 0, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, + ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, + ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, + ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, + ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, + ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_MUMIMO_TRIG_STATS = 27, + ATH12K_DBG_HTT_EXT_STATS_FSE_RX = 28, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, + ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, + ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36, + ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37, + ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38, + ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40, + ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41, + ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45, + ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46, + ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49, + ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51, + ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54, + + /* keep this last */ + ATH12K_DBG_HTT_NUM_EXT_STATS, +}; + +enum ath12k_dbg_htt_tlv_tag { + HTT_STATS_TX_PDEV_CMN_TAG = 0, + HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, + HTT_STATS_TX_PDEV_SIFS_TAG = 2, + HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_STRING_TAG = 5, + HTT_STATS_TX_TQM_GEN_MPDU_TAG = 11, + HTT_STATS_TX_TQM_LIST_MPDU_TAG = 12, + HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG = 13, + HTT_STATS_TX_TQM_CMN_TAG = 14, + HTT_STATS_TX_TQM_PDEV_TAG = 15, + HTT_STATS_TX_DE_EAPOL_PACKETS_TAG = 17, + HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG = 18, + HTT_STATS_TX_DE_CLASSIFY_STATS_TAG = 19, + HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG = 20, + HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG = 21, + HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG = 22, + HTT_STATS_TX_DE_CMN_TAG = 23, + HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25, + HTT_STATS_SFM_CMN_TAG = 26, + HTT_STATS_SRING_STATS_TAG = 27, + HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, + HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35, + HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, + HTT_STATS_TX_SCHED_CMN_TAG = 37, + HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, + HTT_STATS_SFM_CLIENT_USER_TAG = 41, + HTT_STATS_SFM_CLIENT_TAG = 42, + HTT_STATS_TX_TQM_ERROR_STATS_TAG = 43, + HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, + HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG = 46, + HTT_STATS_TX_SELFGEN_CMN_STATS_TAG = 47, + HTT_STATS_TX_SELFGEN_AC_STATS_TAG = 48, + HTT_STATS_TX_SELFGEN_AX_STATS_TAG = 49, + HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG = 50, + HTT_STATS_HW_INTR_MISC_TAG = 54, + HTT_STATS_HW_PDEV_ERRS_TAG = 56, + HTT_STATS_TX_DE_COMPL_STATS_TAG = 65, + HTT_STATS_WHAL_TX_TAG = 66, + HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, + HTT_STATS_PDEV_CCA_1SEC_HIST_TAG = 70, + HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG = 71, + HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72, + HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73, + HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74, + HTT_STATS_TX_SOUNDING_STATS_TAG = 80, + HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, + HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, + HTT_STATS_PDEV_OBSS_PD_TAG = 88, + HTT_STATS_HW_WAR_TAG = 89, + HTT_STATS_LATENCY_PROF_STATS_TAG = 91, + HTT_STATS_LATENCY_CTX_TAG = 92, + HTT_STATS_LATENCY_CNT_TAG = 93, + HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG = 94, + HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG = 95, + HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG = 97, + HTT_STATS_RX_FSE_STATS_TAG = 98, + HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, + HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, + HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, + HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG = 108, + HTT_STATS_TX_SELFGEN_AC_SCHED_STATUS_STATS_TAG = 111, + HTT_STATS_TX_SELFGEN_AX_SCHED_STATUS_STATS_TAG = 112, + HTT_STATS_DLPAGER_STATS_TAG = 120, + HTT_STATS_PHY_COUNTERS_TAG = 121, + HTT_STATS_PHY_STATS_TAG = 122, + HTT_STATS_PHY_RESET_COUNTERS_TAG = 123, + HTT_STATS_PHY_RESET_STATS_TAG = 124, + HTT_STATS_SOC_TXRX_STATS_COMMON_TAG = 125, + HTT_STATS_PER_RATE_STATS_TAG = 128, + HTT_STATS_MU_PPDU_DIST_TAG = 129, + HTT_STATS_TX_PDEV_MUMIMO_GRP_STATS_TAG = 130, + HTT_STATS_AST_ENTRY_TAG = 132, + HTT_STATS_TX_PDEV_RATE_STATS_BE_OFDMA_TAG = 135, + HTT_STATS_TX_SELFGEN_BE_ERR_STATS_TAG = 137, + HTT_STATS_TX_SELFGEN_BE_STATS_TAG = 138, + HTT_STATS_TX_SELFGEN_BE_SCHED_STATUS_STATS_TAG = 139, + HTT_STATS_TXBF_OFDMA_AX_NDPA_STATS_TAG = 147, + HTT_STATS_TXBF_OFDMA_AX_NDP_STATS_TAG = 148, + HTT_STATS_TXBF_OFDMA_AX_BRP_STATS_TAG = 149, + HTT_STATS_TXBF_OFDMA_AX_STEER_STATS_TAG = 150, + HTT_STATS_DMAC_RESET_STATS_TAG = 155, + HTT_STATS_PHY_TPC_STATS_TAG = 157, + HTT_STATS_PDEV_PUNCTURE_STATS_TAG = 158, + HTT_STATS_PDEV_SCHED_ALGO_OFDMA_STATS_TAG = 165, + HTT_STATS_TXBF_OFDMA_AX_STEER_MPDU_STATS_TAG = 172, + HTT_STATS_PDEV_MBSSID_CTRL_FRAME_STATS_TAG = 176, + + HTT_STATS_MAX_TAG, +}; + +#define ATH12K_HTT_STATS_MAC_ID GENMASK(7, 0) + +#define ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_STATS 9 +#define ATH12K_HTT_TX_PDEV_MAX_FLUSH_REASON_STATS 150 + +/* MU MIMO distribution stats is a 2-dimensional array + * with dimension one denoting stats for nr4[0] or nr8[1] + */ +#define ATH12K_HTT_STATS_NUM_NR_BINS 2 +#define ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST 10 +#define ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS 10 +#define ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS 9 +#define ATH12K_HTT_STATS_NUM_SCHED_STATUS_WORDS \ + (ATH12K_HTT_STATS_NUM_NR_BINS * ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS) +#define ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS \ + (ATH12K_HTT_STATS_NUM_NR_BINS * ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST) + +enum ath12k_htt_tx_pdev_underrun_enum { + HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_BETWEEN_MPDU = 1, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_WITHIN_MPDU = 2, + HTT_TX_PDEV_MAX_URRN_STATS = 3, +}; + +enum ath12k_htt_stats_reset_cfg_param_alloc_pos { + ATH12K_HTT_STATS_RESET_PARAM_CFG_32_BYTES = 1, + ATH12K_HTT_STATS_RESET_PARAM_CFG_64_BYTES, + ATH12K_HTT_STATS_RESET_PARAM_CFG_128_BYTES, +}; + +struct debug_htt_stats_req { + bool done; + bool override_cfg_param; + u8 pdev_id; + enum ath12k_dbg_htt_ext_stats_type type; + u32 cfg_param[4]; + u8 peer_addr[ETH_ALEN]; + struct completion htt_stats_rcvd; + u32 buf_len; + u8 buf[]; +}; + +struct ath12k_htt_tx_pdev_stats_cmn_tlv { + __le32 mac_id__word; + __le32 hw_queued; + __le32 hw_reaped; + __le32 underrun; + __le32 hw_paused; + __le32 hw_flush; + __le32 hw_filt; + __le32 tx_abort; + __le32 mpdu_requed; + __le32 tx_xretry; + __le32 data_rc; + __le32 mpdu_dropped_xretry; + __le32 illgl_rate_phy_err; + __le32 cont_xretry; + __le32 tx_timeout; + __le32 pdev_resets; + __le32 phy_underrun; + __le32 txop_ovf; + __le32 seq_posted; + __le32 seq_failed_queueing; + __le32 seq_completed; + __le32 seq_restarted; + __le32 mu_seq_posted; + __le32 seq_switch_hw_paused; + __le32 next_seq_posted_dsr; + __le32 seq_posted_isr; + __le32 seq_ctrl_cached; + __le32 mpdu_count_tqm; + __le32 msdu_count_tqm; + __le32 mpdu_removed_tqm; + __le32 msdu_removed_tqm; + __le32 mpdus_sw_flush; + __le32 mpdus_hw_filter; + __le32 mpdus_truncated; + __le32 mpdus_ack_failed; + __le32 mpdus_expired; + __le32 mpdus_seq_hw_retry; + __le32 ack_tlv_proc; + __le32 coex_abort_mpdu_cnt_valid; + __le32 coex_abort_mpdu_cnt; + __le32 num_total_ppdus_tried_ota; + __le32 num_data_ppdus_tried_ota; + __le32 local_ctrl_mgmt_enqued; + __le32 local_ctrl_mgmt_freed; + __le32 local_data_enqued; + __le32 local_data_freed; + __le32 mpdu_tried; + __le32 isr_wait_seq_posted; + + __le32 tx_active_dur_us_low; + __le32 tx_active_dur_us_high; + __le32 remove_mpdus_max_retries; + __le32 comp_delivered; + __le32 ppdu_ok; + __le32 self_triggers; + __le32 tx_time_dur_data; + __le32 seq_qdepth_repost_stop; + __le32 mu_seq_min_msdu_repost_stop; + __le32 seq_min_msdu_repost_stop; + __le32 seq_txop_repost_stop; + __le32 next_seq_cancel; + __le32 fes_offsets_err_cnt; + __le32 num_mu_peer_blacklisted; + __le32 mu_ofdma_seq_posted; + __le32 ul_mumimo_seq_posted; + __le32 ul_ofdma_seq_posted; + + __le32 thermal_suspend_cnt; + __le32 dfs_suspend_cnt; + __le32 tx_abort_suspend_cnt; + __le32 tgt_specific_opaque_txq_suspend_info; + __le32 last_suspend_reason; +} __packed; + +struct ath12k_htt_tx_pdev_stats_urrn_tlv { + DECLARE_FLEX_ARRAY(__le32, urrn_stats); +} __packed; + +struct ath12k_htt_tx_pdev_stats_flush_tlv { + DECLARE_FLEX_ARRAY(__le32, flush_errs); +} __packed; + +struct ath12k_htt_tx_pdev_stats_phy_err_tlv { + DECLARE_FLEX_ARRAY(__le32, phy_errs); +} __packed; + +struct ath12k_htt_tx_pdev_stats_sifs_tlv { + DECLARE_FLEX_ARRAY(__le32, sifs_status); +} __packed; + +struct ath12k_htt_pdev_ctrl_path_tx_stats_tlv { + __le32 fw_tx_mgmt_subtype[ATH12K_HTT_STATS_SUBTYPE_MAX]; +} __packed; + +struct ath12k_htt_tx_pdev_stats_sifs_hist_tlv { + DECLARE_FLEX_ARRAY(__le32, sifs_hist_status); +} __packed; + +enum ath12k_htt_stats_hw_mode { + ATH12K_HTT_STATS_HWMODE_AC = 0, + ATH12K_HTT_STATS_HWMODE_AX = 1, + ATH12K_HTT_STATS_HWMODE_BE = 2, +}; + +struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv { + __le32 hw_mode; + __le32 num_seq_term_status[ATH12K_HTT_STATS_NUM_SCHED_STATUS_WORDS]; + __le32 num_ppdu_cmpl_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; + __le32 num_seq_posted[ATH12K_HTT_STATS_NUM_NR_BINS]; + __le32 num_ppdu_posted_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; +} __packed; + +#define ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES 7 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LTF 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES 6 + +struct ath12k_htt_tx_pdev_rate_stats_tlv { + __le32 mac_id_word; + __le32 tx_ldpc; + __le32 rts_cnt; + __le32 ack_rssi; + __le32 tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_su_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_mu_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 tx_stbc[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_pream[ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + __le32 tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_dcm[ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS]; + __le32 rts_success; + __le32 tx_legacy_cck_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + __le32 tx_legacy_ofdm_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 ac_mu_mimo_tx_ldpc; + __le32 ax_mu_mimo_tx_ldpc; + __le32 ofdma_tx_ldpc; + __le32 tx_he_ltf[ATH12K_HTT_TX_PDEV_STATS_NUM_LTF]; + __le32 ac_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ax_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ofdma_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ac_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ax_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ofdma_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ac_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ax_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ofdma_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ac_mu_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ax_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ofdma_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 trigger_type_11ax[ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES]; + __le32 tx_11ax_su_ext; + __le32 tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_stbc_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ax_mu_mimo_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ofdma_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ax_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ofd_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_mcs_ext_2[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 tx_bw_320mhz; +}; + +#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES 7 +#define ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER 8 +#define ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS 16 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS 6 +#define ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2 + +struct ath12k_htt_rx_pdev_rate_stats_tlv { + __le32 mac_id_word; + __le32 nsts; + __le32 rx_ldpc; + __le32 rts_cnt; + __le32 rssi_mgmt; + __le32 rssi_data; + __le32 rssi_comb; + __le32 rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_nss[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 rx_dcm[ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS]; + __le32 rx_stbc[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_bw[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_pream[ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + u8 rssi_chain_in_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_gi[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rssi_in_dbm; + __le32 rx_11ax_su_ext; + __le32 rx_11ac_mumimo; + __le32 rx_11ax_mumimo; + __le32 rx_11ax_ofdma; + __le32 txbf; + __le32 rx_legacy_cck_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + __le32 rx_legacy_ofdm_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 rx_active_dur_us_low; + __le32 rx_active_dur_us_high; + __le32 rx_11ax_ul_ofdma; + __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ul_ofdma_rx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ul_ofdma_rx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ul_ofdma_rx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ul_ofdma_rx_stbc; + __le32 ul_ofdma_rx_ldpc; + __le32 rx_ulofdma_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 nss_count; + __le32 pilot_count; + __le32 rx_pil_evm_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS]; + __le32 rx_pilot_evm_db_mean[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + s8 rx_ul_fd_rssi[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 per_chain_rssi_pkt_type; + s8 rx_per_chain_rssi_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_su_ndpa; + __le32 rx_11ax_su_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_mu_ndpa; + __le32 rx_11ax_mu_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_br_poll; + __le32 rx_11ax_dl_ofdma_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_11ax_dl_ofdma_ru[ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS]; + __le32 rx_ulmumimo_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulofdma_non_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; +}; + +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT 14 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS 5 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS 5 + +struct ath12k_htt_rx_pdev_rate_ext_stats_tlv { + u8 rssi_chain_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS]; + s8 rx_per_chain_rssi_ext_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS]; + __le32 rssi_mcast_in_dbm; + __le32 rssi_mgmt_in_dbm; + __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_stbc_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_gi_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 ul_ofdma_rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 ul_ofdma_rx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_su_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_mu_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_dl_ofdma_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_mcs_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 rx_bw_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS]; + __le32 rx_gi_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 rx_su_punctured_mode[ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS]; +}; + +#define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) +#define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) + +#define ATH12K_HTT_TX_PDEV_NUM_SCHED_ORDER_LOG 20 + +struct ath12k_htt_stats_tx_sched_cmn_tlv { + __le32 mac_id__word; + __le32 current_timestamp; +} __packed; + +struct ath12k_htt_tx_pdev_stats_sched_per_txq_tlv { + __le32 mac_id__word; + __le32 sched_policy; + __le32 last_sched_cmd_posted_timestamp; + __le32 last_sched_cmd_compl_timestamp; + __le32 sched_2_tac_lwm_count; + __le32 sched_2_tac_ring_full; + __le32 sched_cmd_post_failure; + __le32 num_active_tids; + __le32 num_ps_schedules; + __le32 sched_cmds_pending; + __le32 num_tid_register; + __le32 num_tid_unregister; + __le32 num_qstats_queried; + __le32 qstats_update_pending; + __le32 last_qstats_query_timestamp; + __le32 num_tqm_cmdq_full; + __le32 num_de_sched_algo_trigger; + __le32 num_rt_sched_algo_trigger; + __le32 num_tqm_sched_algo_trigger; + __le32 notify_sched; + __le32 dur_based_sendn_term; + __le32 su_notify2_sched; + __le32 su_optimal_queued_msdus_sched; + __le32 su_delay_timeout_sched; + __le32 su_min_txtime_sched_delay; + __le32 su_no_delay; + __le32 num_supercycles; + __le32 num_subcycles_with_sort; + __le32 num_subcycles_no_sort; +} __packed; + +struct ath12k_htt_sched_txq_cmd_posted_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_cmd_posted); +} __packed; + +struct ath12k_htt_sched_txq_cmd_reaped_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_cmd_reaped); +} __packed; + +struct ath12k_htt_sched_txq_sched_order_su_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_order_su); +} __packed; + +struct ath12k_htt_sched_txq_sched_ineligibility_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_ineligibility); +} __packed; + +enum ath12k_htt_sched_txq_supercycle_triggers_tlv_enum { + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_NONE = 0, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_FORCED, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_LESS_NUM_TIDQ_ENTRIES, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_LESS_NUM_ACTIVE_TIDS, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_MAX_ITR_REACHED, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_DUR_THRESHOLD_REACHED, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_TWT_TRIGGER, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_MAX, +}; + +struct ath12k_htt_sched_txq_supercycle_triggers_tlv { + DECLARE_FLEX_ARRAY(__le32, supercycle_triggers); +} __packed; + +struct ath12k_htt_hw_stats_pdev_errs_tlv { + __le32 mac_id__word; + __le32 tx_abort; + __le32 tx_abort_fail_count; + __le32 rx_abort; + __le32 rx_abort_fail_count; + __le32 warm_reset; + __le32 cold_reset; + __le32 tx_flush; + __le32 tx_glb_reset; + __le32 tx_txq_reset; + __le32 rx_timeout_reset; + __le32 mac_cold_reset_restore_cal; + __le32 mac_cold_reset; + __le32 mac_warm_reset; + __le32 mac_only_reset; + __le32 phy_warm_reset; + __le32 phy_warm_reset_ucode_trig; + __le32 mac_warm_reset_restore_cal; + __le32 mac_sfm_reset; + __le32 phy_warm_reset_m3_ssr; + __le32 phy_warm_reset_reason_phy_m3; + __le32 phy_warm_reset_reason_tx_hw_stuck; + __le32 phy_warm_reset_reason_num_rx_frame_stuck; + __le32 phy_warm_reset_reason_wal_rx_rec_rx_busy; + __le32 phy_warm_reset_reason_wal_rx_rec_mac_hng; + __le32 phy_warm_reset_reason_mac_conv_phy_reset; + __le32 wal_rx_recovery_rst_mac_hang_cnt; + __le32 wal_rx_recovery_rst_known_sig_cnt; + __le32 wal_rx_recovery_rst_no_rx_cnt; + __le32 wal_rx_recovery_rst_no_rx_consec_cnt; + __le32 wal_rx_recovery_rst_rx_busy_cnt; + __le32 wal_rx_recovery_rst_phy_mac_hang_cnt; + __le32 rx_flush_cnt; + __le32 phy_warm_reset_reason_tx_exp_cca_stuck; + __le32 phy_warm_reset_reason_tx_consec_flsh_war; + __le32 phy_warm_reset_reason_tx_hwsch_reset_war; + __le32 phy_warm_reset_reason_hwsch_cca_wdog_war; + __le32 fw_rx_rings_reset; + __le32 rx_dest_drain_rx_descs_leak_prevented; + __le32 rx_dest_drain_rx_descs_saved_cnt; + __le32 rx_dest_drain_rxdma2reo_leak_detected; + __le32 rx_dest_drain_rxdma2fw_leak_detected; + __le32 rx_dest_drain_rxdma2wbm_leak_detected; + __le32 rx_dest_drain_rxdma1_2sw_leak_detected; + __le32 rx_dest_drain_rx_drain_ok_mac_idle; + __le32 rx_dest_drain_ok_mac_not_idle; + __le32 rx_dest_drain_prerequisite_invld; + __le32 rx_dest_drain_skip_non_lmac_reset; + __le32 rx_dest_drain_hw_fifo_notempty_post_wait; +} __packed; + +#define ATH12K_HTT_STATS_MAX_HW_INTR_NAME_LEN 8 +struct ath12k_htt_hw_stats_intr_misc_tlv { + u8 hw_intr_name[ATH12K_HTT_STATS_MAX_HW_INTR_NAME_LEN]; + __le32 mask; + __le32 count; +} __packed; + +struct ath12k_htt_hw_stats_whal_tx_tlv { + __le32 mac_id__word; + __le32 last_unpause_ppdu_id; + __le32 hwsch_unpause_wait_tqm_write; + __le32 hwsch_dummy_tlv_skipped; + __le32 hwsch_misaligned_offset_received; + __le32 hwsch_reset_count; + __le32 hwsch_dev_reset_war; + __le32 hwsch_delayed_pause; + __le32 hwsch_long_delayed_pause; + __le32 sch_rx_ppdu_no_response; + __le32 sch_selfgen_response; + __le32 sch_rx_sifs_resp_trigger; +} __packed; + +struct ath12k_htt_hw_war_stats_tlv { + __le32 mac_id__word; + DECLARE_FLEX_ARRAY(__le32, hw_wars); +} __packed; + +struct ath12k_htt_tx_tqm_cmn_stats_tlv { + __le32 mac_id__word; + __le32 max_cmdq_id; + __le32 list_mpdu_cnt_hist_intvl; + __le32 add_msdu; + __le32 q_empty; + __le32 q_not_empty; + __le32 drop_notification; + __le32 desc_threshold; + __le32 hwsch_tqm_invalid_status; + __le32 missed_tqm_gen_mpdus; + __le32 tqm_active_tids; + __le32 tqm_inactive_tids; + __le32 tqm_active_msduq_flows; + __le32 msduq_timestamp_updates; + __le32 msduq_updates_mpdu_head_info_cmd; + __le32 msduq_updates_emp_to_nonemp_status; + __le32 get_mpdu_head_info_cmds_by_query; + __le32 get_mpdu_head_info_cmds_by_tac; + __le32 gen_mpdu_cmds_by_query; + __le32 high_prio_q_not_empty; +} __packed; + +struct ath12k_htt_tx_tqm_error_stats_tlv { + __le32 q_empty_failure; + __le32 q_not_empty_failure; + __le32 add_msdu_failure; + __le32 tqm_cache_ctl_err; + __le32 tqm_soft_reset; + __le32 tqm_reset_num_in_use_link_descs; + __le32 tqm_reset_num_lost_link_descs; + __le32 tqm_reset_num_lost_host_tx_buf_cnt; + __le32 tqm_reset_num_in_use_internal_tqm; + __le32 tqm_reset_num_in_use_idle_link_rng; + __le32 tqm_reset_time_to_tqm_hang_delta_ms; + __le32 tqm_reset_recovery_time_ms; + __le32 tqm_reset_num_peers_hdl; + __le32 tqm_reset_cumm_dirty_hw_mpduq_cnt; + __le32 tqm_reset_cumm_dirty_hw_msduq_proc; + __le32 tqm_reset_flush_cache_cmd_su_cnt; + __le32 tqm_reset_flush_cache_cmd_other_cnt; + __le32 tqm_reset_flush_cache_cmd_trig_type; + __le32 tqm_reset_flush_cache_cmd_trig_cfg; + __le32 tqm_reset_flush_cmd_skp_status_null; +} __packed; + +struct ath12k_htt_tx_tqm_gen_mpdu_stats_tlv { + DECLARE_FLEX_ARRAY(__le32, gen_mpdu_end_reason); +} __packed; + +#define ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_END_REASON 16 +#define ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS 16 + +struct ath12k_htt_tx_tqm_list_mpdu_stats_tlv { + DECLARE_FLEX_ARRAY(__le32, list_mpdu_end_reason); +} __packed; + +struct ath12k_htt_tx_tqm_list_mpdu_cnt_tlv { + DECLARE_FLEX_ARRAY(__le32, list_mpdu_cnt_hist); +} __packed; + +struct ath12k_htt_tx_tqm_pdev_stats_tlv { + __le32 msdu_count; + __le32 mpdu_count; + __le32 remove_msdu; + __le32 remove_mpdu; + __le32 remove_msdu_ttl; + __le32 send_bar; + __le32 bar_sync; + __le32 notify_mpdu; + __le32 sync_cmd; + __le32 write_cmd; + __le32 hwsch_trigger; + __le32 ack_tlv_proc; + __le32 gen_mpdu_cmd; + __le32 gen_list_cmd; + __le32 remove_mpdu_cmd; + __le32 remove_mpdu_tried_cmd; + __le32 mpdu_queue_stats_cmd; + __le32 mpdu_head_info_cmd; + __le32 msdu_flow_stats_cmd; + __le32 remove_msdu_cmd; + __le32 remove_msdu_ttl_cmd; + __le32 flush_cache_cmd; + __le32 update_mpduq_cmd; + __le32 enqueue; + __le32 enqueue_notify; + __le32 notify_mpdu_at_head; + __le32 notify_mpdu_state_valid; + __le32 sched_udp_notify1; + __le32 sched_udp_notify2; + __le32 sched_nonudp_notify1; + __le32 sched_nonudp_notify2; +} __packed; + +struct ath12k_htt_tx_de_cmn_stats_tlv { + __le32 mac_id__word; + __le32 tcl2fw_entry_count; + __le32 not_to_fw; + __le32 invalid_pdev_vdev_peer; + __le32 tcl_res_invalid_addrx; + __le32 wbm2fw_entry_count; + __le32 invalid_pdev; + __le32 tcl_res_addrx_timeout; + __le32 invalid_vdev; + __le32 invalid_tcl_exp_frame_desc; + __le32 vdev_id_mismatch_cnt; +} __packed; + +struct ath12k_htt_tx_de_eapol_packets_stats_tlv { + __le32 m1_packets; + __le32 m2_packets; + __le32 m3_packets; + __le32 m4_packets; + __le32 g1_packets; + __le32 g2_packets; + __le32 rc4_packets; + __le32 eap_packets; + __le32 eapol_start_packets; + __le32 eapol_logoff_packets; + __le32 eapol_encap_asf_packets; +} __packed; + +struct ath12k_htt_tx_de_classify_stats_tlv { + __le32 arp_packets; + __le32 igmp_packets; + __le32 dhcp_packets; + __le32 host_inspected; + __le32 htt_included; + __le32 htt_valid_mcs; + __le32 htt_valid_nss; + __le32 htt_valid_preamble_type; + __le32 htt_valid_chainmask; + __le32 htt_valid_guard_interval; + __le32 htt_valid_retries; + __le32 htt_valid_bw_info; + __le32 htt_valid_power; + __le32 htt_valid_key_flags; + __le32 htt_valid_no_encryption; + __le32 fse_entry_count; + __le32 fse_priority_be; + __le32 fse_priority_high; + __le32 fse_priority_low; + __le32 fse_traffic_ptrn_be; + __le32 fse_traffic_ptrn_over_sub; + __le32 fse_traffic_ptrn_bursty; + __le32 fse_traffic_ptrn_interactive; + __le32 fse_traffic_ptrn_periodic; + __le32 fse_hwqueue_alloc; + __le32 fse_hwqueue_created; + __le32 fse_hwqueue_send_to_host; + __le32 mcast_entry; + __le32 bcast_entry; + __le32 htt_update_peer_cache; + __le32 htt_learning_frame; + __le32 fse_invalid_peer; + __le32 mec_notify; +} __packed; + +struct ath12k_htt_tx_de_classify_failed_stats_tlv { + __le32 ap_bss_peer_not_found; + __le32 ap_bcast_mcast_no_peer; + __le32 sta_delete_in_progress; + __le32 ibss_no_bss_peer; + __le32 invalid_vdev_type; + __le32 invalid_ast_peer_entry; + __le32 peer_entry_invalid; + __le32 ethertype_not_ip; + __le32 eapol_lookup_failed; + __le32 qpeer_not_allow_data; + __le32 fse_tid_override; + __le32 ipv6_jumbogram_zero_length; + __le32 qos_to_non_qos_in_prog; + __le32 ap_bcast_mcast_eapol; + __le32 unicast_on_ap_bss_peer; + __le32 ap_vdev_invalid; + __le32 incomplete_llc; + __le32 eapol_duplicate_m3; + __le32 eapol_duplicate_m4; +} __packed; + +struct ath12k_htt_tx_de_classify_status_stats_tlv { + __le32 eok; + __le32 classify_done; + __le32 lookup_failed; + __le32 send_host_dhcp; + __le32 send_host_mcast; + __le32 send_host_unknown_dest; + __le32 send_host; + __le32 status_invalid; +} __packed; + +struct ath12k_htt_tx_de_enqueue_packets_stats_tlv { + __le32 enqueued_pkts; + __le32 to_tqm; + __le32 to_tqm_bypass; +} __packed; + +struct ath12k_htt_tx_de_enqueue_discard_stats_tlv { + __le32 discarded_pkts; + __le32 local_frames; + __le32 is_ext_msdu; +} __packed; + +struct ath12k_htt_tx_de_compl_stats_tlv { + __le32 tcl_dummy_frame; + __le32 tqm_dummy_frame; + __le32 tqm_notify_frame; + __le32 fw2wbm_enq; + __le32 tqm_bypass_frame; +} __packed; + +enum ath12k_htt_tx_mumimo_grp_invalid_reason_code_stats { + ATH12K_HTT_TX_MUMIMO_GRP_VALID, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_NUM_MU_USERS_EXCEEDED_MU_MAX_USERS, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_SCHED_ALGO_NOT_MU_COMPATIBLE_GID, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_NON_PRIMARY_GRP, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_ZERO_CANDIDATES, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_MORE_CANDIDATES, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_GROUP_SIZE_EXCEED_NSS, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_GROUP_INELIGIBLE, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_GROUP_EFF_MU_TPUT_OMBPS, + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_MAX_REASON_CODE, +}; + +#define ATH12K_HTT_NUM_AC_WMM 0x4 +#define ATH12K_HTT_MAX_NUM_SBT_INTR 4 +#define ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS 4 +#define ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS 7 +#define ATH12K_HTT_TX_NUM_OFDMA_USER_STATS 74 +#define ATH12K_HTT_TX_NUM_UL_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ 8 +#define ATH12K_HTT_STATS_MUMIMO_TPUT_NUM_BINS 10 + +#define ATH12K_HTT_STATS_MAX_INVALID_REASON_CODE \ + ATH12K_HTT_TX_MUMIMO_GRP_INVALID_MAX_REASON_CODE +#define ATH12K_HTT_TX_NUM_MUMIMO_GRP_INVALID_WORDS \ + (ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ * ATH12K_HTT_STATS_MAX_INVALID_REASON_CODE) + +struct ath12k_htt_tx_selfgen_cmn_stats_tlv { + __le32 mac_id__word; + __le32 su_bar; + __le32 rts; + __le32 cts2self; + __le32 qos_null; + __le32 delayed_bar_1; + __le32 delayed_bar_2; + __le32 delayed_bar_3; + __le32 delayed_bar_4; + __le32 delayed_bar_5; + __le32 delayed_bar_6; + __le32 delayed_bar_7; +} __packed; + +struct ath12k_htt_tx_selfgen_ac_stats_tlv { + __le32 ac_su_ndpa; + __le32 ac_su_ndp; + __le32 ac_mu_mimo_ndpa; + __le32 ac_mu_mimo_ndp; + __le32 ac_mu_mimo_brpoll[ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS - 1]; +} __packed; + +struct ath12k_htt_tx_selfgen_ax_stats_tlv { + __le32 ax_su_ndpa; + __le32 ax_su_ndp; + __le32 ax_mu_mimo_ndpa; + __le32 ax_mu_mimo_ndp; + __le32 ax_mu_mimo_brpoll[ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS - 1]; + __le32 ax_basic_trigger; + __le32 ax_bsr_trigger; + __le32 ax_mu_bar_trigger; + __le32 ax_mu_rts_trigger; + __le32 ax_ulmumimo_trigger; +} __packed; + +struct ath12k_htt_tx_selfgen_be_stats_tlv { + __le32 be_su_ndpa; + __le32 be_su_ndp; + __le32 be_mu_mimo_ndpa; + __le32 be_mu_mimo_ndp; + __le32 be_mu_mimo_brpoll[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1]; + __le32 be_basic_trigger; + __le32 be_bsr_trigger; + __le32 be_mu_bar_trigger; + __le32 be_mu_rts_trigger; + __le32 be_ulmumimo_trigger; + __le32 be_su_ndpa_queued; + __le32 be_su_ndp_queued; + __le32 be_mu_mimo_ndpa_queued; + __le32 be_mu_mimo_ndp_queued; + __le32 be_mu_mimo_brpoll_queued[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1]; + __le32 be_ul_mumimo_trigger[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; +} __packed; + +struct ath12k_htt_tx_selfgen_ac_err_stats_tlv { + __le32 ac_su_ndp_err; + __le32 ac_su_ndpa_err; + __le32 ac_mu_mimo_ndpa_err; + __le32 ac_mu_mimo_ndp_err; + __le32 ac_mu_mimo_brp1_err; + __le32 ac_mu_mimo_brp2_err; + __le32 ac_mu_mimo_brp3_err; +} __packed; + +struct ath12k_htt_tx_selfgen_ax_err_stats_tlv { + __le32 ax_su_ndp_err; + __le32 ax_su_ndpa_err; + __le32 ax_mu_mimo_ndpa_err; + __le32 ax_mu_mimo_ndp_err; + __le32 ax_mu_mimo_brp_err[ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS - 1]; + __le32 ax_basic_trigger_err; + __le32 ax_bsr_trigger_err; + __le32 ax_mu_bar_trigger_err; + __le32 ax_mu_rts_trigger_err; + __le32 ax_ulmumimo_trigger_err; +} __packed; + +struct ath12k_htt_tx_selfgen_be_err_stats_tlv { + __le32 be_su_ndp_err; + __le32 be_su_ndpa_err; + __le32 be_mu_mimo_ndpa_err; + __le32 be_mu_mimo_ndp_err; + __le32 be_mu_mimo_brp_err[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1]; + __le32 be_basic_trigger_err; + __le32 be_bsr_trigger_err; + __le32 be_mu_bar_trigger_err; + __le32 be_mu_rts_trigger_err; + __le32 be_ulmumimo_trigger_err; + __le32 be_mu_mimo_brp_err_num_cbf_rxd[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; + __le32 be_su_ndpa_flushed; + __le32 be_su_ndp_flushed; + __le32 be_mu_mimo_ndpa_flushed; + __le32 be_mu_mimo_ndp_flushed; + __le32 be_mu_mimo_brpoll_flushed[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS - 1]; + __le32 be_ul_mumimo_trigger_err[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; +} __packed; + +enum ath12k_htt_tx_selfgen_sch_tsflag_error_stats { + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_FLUSH_RCVD_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_FILT_SCHED_CMD_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_RESP_MISMATCH_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_RESP_CBF_MIMO_CTRL_MISMATCH_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_RESP_CBF_BW_MISMATCH_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_RETRY_COUNT_FAIL_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_RESP_TOO_LATE_RECEIVED_ERR, + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_SIFS_STALL_NO_NEXT_CMD_ERR, + + ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS +}; + +struct ath12k_htt_tx_selfgen_ac_sched_status_stats_tlv { + __le32 ac_su_ndpa_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ac_su_ndp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ac_su_ndp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ac_mu_mimo_ndpa_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ac_mu_mimo_ndp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ac_mu_mimo_ndp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ac_mu_mimo_brp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ac_mu_mimo_brp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; +} __packed; + +struct ath12k_htt_tx_selfgen_ax_sched_status_stats_tlv { + __le32 ax_su_ndpa_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_su_ndp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_su_ndp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ax_mu_mimo_ndpa_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_mu_mimo_ndp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_mu_mimo_ndp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ax_mu_brp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_mu_brp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ax_mu_bar_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_mu_bar_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ax_basic_trig_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_basic_trig_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 ax_ulmumimo_trig_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 ax_ulmumimo_trig_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; +} __packed; + +struct ath12k_htt_tx_selfgen_be_sched_status_stats_tlv { + __le32 be_su_ndpa_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_su_ndp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_su_ndp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 be_mu_mimo_ndpa_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_mu_mimo_ndp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_mu_mimo_ndp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 be_mu_brp_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_mu_brp_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 be_mu_bar_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_mu_bar_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 be_basic_trig_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_basic_trig_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; + __le32 be_ulmumimo_trig_sch_status[ATH12K_HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + __le32 be_ulmumimo_trig_sch_flag_err[ATH12K_HTT_TX_SELFGEN_SCH_TSFLAG_ERR_STATS]; +} __packed; + +struct ath12k_htt_stats_string_tlv { + DECLARE_FLEX_ARRAY(__le32, data); +} __packed; + +#define ATH12K_HTT_SRING_STATS_MAC_ID GENMASK(7, 0) +#define ATH12K_HTT_SRING_STATS_RING_ID GENMASK(15, 8) +#define ATH12K_HTT_SRING_STATS_ARENA GENMASK(23, 16) +#define ATH12K_HTT_SRING_STATS_EP BIT(24) +#define ATH12K_HTT_SRING_STATS_NUM_AVAIL_WORDS GENMASK(15, 0) +#define ATH12K_HTT_SRING_STATS_NUM_VALID_WORDS GENMASK(31, 16) +#define ATH12K_HTT_SRING_STATS_HEAD_PTR GENMASK(15, 0) +#define ATH12K_HTT_SRING_STATS_TAIL_PTR GENMASK(31, 16) +#define ATH12K_HTT_SRING_STATS_CONSUMER_EMPTY GENMASK(15, 0) +#define ATH12K_HTT_SRING_STATS_PRODUCER_FULL GENMASK(31, 16) +#define ATH12K_HTT_SRING_STATS_PREFETCH_COUNT GENMASK(15, 0) +#define ATH12K_HTT_SRING_STATS_INTERNAL_TAIL_PTR GENMASK(31, 16) + +struct ath12k_htt_sring_stats_tlv { + __le32 mac_id__ring_id__arena__ep; + __le32 base_addr_lsb; + __le32 base_addr_msb; + __le32 ring_size; + __le32 elem_size; + __le32 num_avail_words__num_valid_words; + __le32 head_ptr__tail_ptr; + __le32 consumer_empty__producer_full; + __le32 prefetch_count__internal_tail_ptr; +} __packed; + +struct ath12k_htt_sfm_cmn_tlv { + __le32 mac_id__word; + __le32 buf_total; + __le32 mem_empty; + __le32 deallocate_bufs; + __le32 num_records; +} __packed; + +struct ath12k_htt_sfm_client_tlv { + __le32 client_id; + __le32 buf_min; + __le32 buf_max; + __le32 buf_busy; + __le32 buf_alloc; + __le32 buf_avail; + __le32 num_users; +} __packed; + +struct ath12k_htt_sfm_client_user_tlv { + DECLARE_FLEX_ARRAY(__le32, dwords_used_by_user_n); +} __packed; + +struct ath12k_htt_tx_pdev_mu_mimo_sch_stats_tlv { + __le32 mu_mimo_sch_posted; + __le32 mu_mimo_sch_failed; + __le32 mu_mimo_ppdu_posted; + __le32 ac_mu_mimo_sch_nusers[ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS]; + __le32 ax_mu_mimo_sch_nusers[ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS]; + __le32 ax_ofdma_sch_nusers[ATH12K_HTT_TX_NUM_OFDMA_USER_STATS]; + __le32 ax_ul_ofdma_nusers[ATH12K_HTT_TX_NUM_OFDMA_USER_STATS]; + __le32 ax_ul_ofdma_bsr_nusers[ATH12K_HTT_TX_NUM_OFDMA_USER_STATS]; + __le32 ax_ul_ofdma_bar_nusers[ATH12K_HTT_TX_NUM_OFDMA_USER_STATS]; + __le32 ax_ul_ofdma_brp_nusers[ATH12K_HTT_TX_NUM_OFDMA_USER_STATS]; + __le32 ax_ul_mumimo_nusers[ATH12K_HTT_TX_NUM_UL_MUMIMO_USER_STATS]; + __le32 ax_ul_mumimo_brp_nusers[ATH12K_HTT_TX_NUM_UL_MUMIMO_USER_STATS]; + __le32 ac_mu_mimo_per_grp_sz[ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS]; + __le32 ax_mu_mimo_per_grp_sz[ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS]; + __le32 be_mu_mimo_sch_nusers[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; + __le32 be_mu_mimo_per_grp_sz[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; + __le32 ac_mu_mimo_grp_sz_ext[ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS]; +} __packed; + +struct ath12k_htt_tx_pdev_mumimo_grp_stats_tlv { + __le32 dl_mumimo_grp_best_grp_size[ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ]; + __le32 dl_mumimo_grp_best_num_usrs[ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS]; + __le32 dl_mumimo_grp_eligible[ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ]; + __le32 dl_mumimo_grp_ineligible[ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ]; + __le32 dl_mumimo_grp_invalid[ATH12K_HTT_TX_NUM_MUMIMO_GRP_INVALID_WORDS]; + __le32 dl_mumimo_grp_tputs[ATH12K_HTT_STATS_MUMIMO_TPUT_NUM_BINS]; + __le32 ul_mumimo_grp_best_grp_size[ATH12K_HTT_STATS_NUM_MAX_MUMIMO_SZ]; + __le32 ul_mumimo_grp_best_usrs[ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS]; + __le32 ul_mumimo_grp_tputs[ATH12K_HTT_STATS_MUMIMO_TPUT_NUM_BINS]; +} __packed; + +enum ath12k_htt_stats_tx_sched_modes { + ATH12K_HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC = 0, + ATH12K_HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX, + ATH12K_HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX, + ATH12K_HTT_STATS_TX_SCHED_MODE_MU_OFDMA_BE, + ATH12K_HTT_STATS_TX_SCHED_MODE_MU_MIMO_BE +}; + +struct ath12k_htt_tx_pdev_mpdu_stats_tlv { + __le32 mpdus_queued_usr; + __le32 mpdus_tried_usr; + __le32 mpdus_failed_usr; + __le32 mpdus_requeued_usr; + __le32 err_no_ba_usr; + __le32 mpdu_underrun_usr; + __le32 ampdu_underrun_usr; + __le32 user_index; + __le32 tx_sched_mode; +} __packed; + +struct ath12k_htt_pdev_stats_cca_counters_tlv { + __le32 tx_frame_usec; + __le32 rx_frame_usec; + __le32 rx_clear_usec; + __le32 my_rx_frame_usec; + __le32 usec_cnt; + __le32 med_rx_idle_usec; + __le32 med_tx_idle_global_usec; + __le32 cca_obss_usec; +} __packed; + +struct ath12k_htt_pdev_cca_stats_hist_v1_tlv { + __le32 chan_num; + __le32 num_records; + __le32 valid_cca_counters_bitmap; + __le32 collection_interval; +} __packed; + +#define ATH12K_HTT_TX_CV_CORR_MAX_NUM_COLUMNS 8 +#define ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS 4 +#define ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_TX_NUM_MCS_CNTRS 12 +#define ATH12K_HTT_TX_NUM_EXTRA_MCS_CNTRS 2 + +#define ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \ + (ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \ + ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS) + +enum ath12k_htt_txbf_sound_steer_modes { + ATH12K_HTT_IMPL_STEER_STATS = 0, + ATH12K_HTT_EXPL_SUSIFS_STEER_STATS = 1, + ATH12K_HTT_EXPL_SURBO_STEER_STATS = 2, + ATH12K_HTT_EXPL_MUSIFS_STEER_STATS = 3, + ATH12K_HTT_EXPL_MURBO_STEER_STATS = 4, + ATH12K_HTT_TXBF_MAX_NUM_OF_MODES = 5 +}; + +enum ath12k_htt_stats_sounding_tx_mode { + ATH12K_HTT_TX_AC_SOUNDING_MODE = 0, + ATH12K_HTT_TX_AX_SOUNDING_MODE = 1, + ATH12K_HTT_TX_BE_SOUNDING_MODE = 2, + ATH12K_HTT_TX_CMN_SOUNDING_MODE = 3, +}; + +struct ath12k_htt_tx_sounding_stats_tlv { + __le32 tx_sounding_mode; + __le32 cbf_20[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_40[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_80[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_160[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 sounding[ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS]; + __le32 cv_nc_mismatch_err; + __le32 cv_fcs_err; + __le32 cv_frag_idx_mismatch; + __le32 cv_invalid_peer_id; + __le32 cv_no_txbf_setup; + __le32 cv_expiry_in_update; + __le32 cv_pkt_bw_exceed; + __le32 cv_dma_not_done_err; + __le32 cv_update_failed; + __le32 cv_total_query; + __le32 cv_total_pattern_query; + __le32 cv_total_bw_query; + __le32 cv_invalid_bw_coding; + __le32 cv_forced_sounding; + __le32 cv_standalone_sounding; + __le32 cv_nc_mismatch; + __le32 cv_fb_type_mismatch; + __le32 cv_ofdma_bw_mismatch; + __le32 cv_bw_mismatch; + __le32 cv_pattern_mismatch; + __le32 cv_preamble_mismatch; + __le32 cv_nr_mismatch; + __le32 cv_in_use_cnt_exceeded; + __le32 cv_found; + __le32 cv_not_found; + __le32 sounding_320[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; + __le32 cbf_320[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cv_ntbr_sounding; + __le32 cv_found_upload_in_progress; + __le32 cv_expired_during_query; + __le32 cv_dma_timeout_error; + __le32 cv_buf_ibf_uploads; + __le32 cv_buf_ebf_uploads; + __le32 cv_buf_received; + __le32 cv_buf_fed_back; + __le32 cv_total_query_ibf; + __le32 cv_found_ibf; + __le32 cv_not_found_ibf; + __le32 cv_expired_during_query_ibf; +} __packed; + +struct ath12k_htt_pdev_obss_pd_stats_tlv { + __le32 num_obss_tx_ppdu_success; + __le32 num_obss_tx_ppdu_failure; + __le32 num_sr_tx_transmissions; + __le32 num_spatial_reuse_opportunities; + __le32 num_non_srg_opportunities; + __le32 num_non_srg_ppdu_tried; + __le32 num_non_srg_ppdu_success; + __le32 num_srg_opportunities; + __le32 num_srg_ppdu_tried; + __le32 num_srg_ppdu_success; + __le32 num_psr_opportunities; + __le32 num_psr_ppdu_tried; + __le32 num_psr_ppdu_success; + __le32 num_non_srg_tried_per_ac[ATH12K_HTT_NUM_AC_WMM]; + __le32 num_non_srg_success_ac[ATH12K_HTT_NUM_AC_WMM]; + __le32 num_srg_tried_per_ac[ATH12K_HTT_NUM_AC_WMM]; + __le32 num_srg_success_per_ac[ATH12K_HTT_NUM_AC_WMM]; + __le32 num_obss_min_dur_check_flush_cnt; + __le32 num_sr_ppdu_abort_flush_cnt; +} __packed; + +#define ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN 32 +#define ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST 3 +#define ATH12K_HTT_INTERRUPTS_LATENCY_PROFILE_MAX_HIST 3 + +struct ath12k_htt_latency_prof_stats_tlv { + __le32 print_header; + s8 latency_prof_name[ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN]; + __le32 cnt; + __le32 min; + __le32 max; + __le32 last; + __le32 tot; + __le32 avg; + __le32 hist_intvl; + __le32 hist[ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST]; +} __packed; + +struct ath12k_htt_latency_prof_ctx_tlv { + __le32 duration; + __le32 tx_msdu_cnt; + __le32 tx_mpdu_cnt; + __le32 tx_ppdu_cnt; + __le32 rx_msdu_cnt; + __le32 rx_mpdu_cnt; +} __packed; + +struct ath12k_htt_latency_prof_cnt_tlv { + __le32 prof_enable_cnt; +} __packed; + +#define ATH12K_HTT_RX_NUM_MCS_CNTRS 12 +#define ATH12K_HTT_RX_NUM_GI_CNTRS 4 +#define ATH12K_HTT_RX_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_RX_NUM_BW_CNTRS 4 +#define ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS 6 +#define ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS 7 +#define ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK 5 +#define ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES 2 +#define ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS 2 + +enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE { + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2, + ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS, +}; + +struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv { + __le32 user_index; + __le32 rx_ulofdma_non_data_ppdu; + __le32 rx_ulofdma_data_ppdu; + __le32 rx_ulofdma_mpdu_ok; + __le32 rx_ulofdma_mpdu_fail; + __le32 rx_ulofdma_non_data_nusers; + __le32 rx_ulofdma_data_nusers; +} __packed; + +struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv { + __le32 mac_id__word; + __le32 rx_11ax_ul_ofdma; + __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_ofdma_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_ofdma_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 ul_ofdma_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_ofdma_rx_stbc; + __le32 ul_ofdma_rx_ldpc; + __le32 data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS]; + __le32 non_data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS]; + __le32 uplink_sta_aid[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_target_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_fd_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_power_headroom[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_ofdma_bsc_trig_rx_qos_null_only; +} __packed; + +#define ATH12K_HTT_TX_UL_MUMIMO_USER_STATS 8 + +struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv { + __le32 mac_id__word; + __le32 rx_11ax_ul_mumimo; + __le32 ul_mumimo_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_mumimo_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 ul_mumimo_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_mumimo_rx_stbc; + __le32 ul_mumimo_rx_ldpc; + __le32 ul_mumimo_rx_mcs_ext[ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS]; + __le32 ul_gi_ext[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS]; + s8 ul_rssi[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS][ATH12K_HTT_RX_NUM_BW_CNTRS]; + s8 tgt_rssi[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_BW_CNTRS]; + s8 fd[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + s8 db[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 mumimo_bsc_trig_rx_qos_null_only; +} __packed; + +#define ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX 10 +#define ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX 10 +#define ATH12K_HTT_RX_NUM_SQUARE_INDEX 6 +#define ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX 4 +#define ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX 4 + +struct ath12k_htt_rx_fse_stats_tlv { + __le32 fse_enable_cnt; + __le32 fse_disable_cnt; + __le32 fse_cache_invalidate_entry_cnt; + __le32 fse_full_cache_invalidate_cnt; + __le32 fse_num_cache_hits_cnt; + __le32 fse_num_searches_cnt; + __le32 fse_cache_occupancy_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX]; + __le32 fse_cache_occupancy_curr_cnt[ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX]; + __le32 fse_search_stat_square_cnt[ATH12K_HTT_RX_NUM_SQUARE_INDEX]; + __le32 fse_search_stat_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX]; + __le32 fse_search_stat_pending_cnt[ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX]; +} __packed; + +#define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_TXBF_NUM_BW_CNTRS 5 +#define ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES 2 + +struct ath12k_htt_pdev_txrate_txbf_stats_tlv { + __le32 tx_su_txbf_mcs[ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS]; + __le32 tx_su_ibf_mcs[ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS]; + __le32 tx_su_ol_mcs[ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS]; + __le32 tx_su_txbf_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 tx_su_ibf_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 tx_su_ol_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 tx_su_txbf_bw[ATH12K_HTT_TXBF_NUM_BW_CNTRS]; + __le32 tx_su_ibf_bw[ATH12K_HTT_TXBF_NUM_BW_CNTRS]; + __le32 tx_su_ol_bw[ATH12K_HTT_TXBF_NUM_BW_CNTRS]; + __le32 tx_legacy_ofdm_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 txbf[ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_TXBF_NUM_BW_CNTRS]; + __le32 ibf[ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_TXBF_NUM_BW_CNTRS]; + __le32 ol[ATH12K_HTT_TXBF_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_TXBF_NUM_BW_CNTRS]; + __le32 txbf_flag_set_mu_mode; + __le32 txbf_flag_set_final_status; + __le32 txbf_flag_not_set_verified_txbf_mode; + __le32 txbf_flag_not_set_disable_p2p_access; + __le32 txbf_flag_not_set_max_nss_in_he160; + __le32 txbf_flag_not_set_disable_uldlofdma; + __le32 txbf_flag_not_set_mcs_threshold_val; + __le32 txbf_flag_not_set_final_status; +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_ndpa_stats_elem_t { + __le32 ax_ofdma_ndpa_queued; + __le32 ax_ofdma_ndpa_tried; + __le32 ax_ofdma_ndpa_flush; + __le32 ax_ofdma_ndpa_err; +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_ndpa_stats_tlv { + __le32 num_elems_ax_ndpa_arr; + __le32 arr_elem_size_ax_ndpa; + DECLARE_FLEX_ARRAY(struct ath12k_htt_txbf_ofdma_ax_ndpa_stats_elem_t, ax_ndpa); +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_ndp_stats_elem_t { + __le32 ax_ofdma_ndp_queued; + __le32 ax_ofdma_ndp_tried; + __le32 ax_ofdma_ndp_flush; + __le32 ax_ofdma_ndp_err; +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_ndp_stats_tlv { + __le32 num_elems_ax_ndp_arr; + __le32 arr_elem_size_ax_ndp; + DECLARE_FLEX_ARRAY(struct ath12k_htt_txbf_ofdma_ax_ndp_stats_elem_t, ax_ndp); +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_brp_stats_elem_t { + __le32 ax_ofdma_brp_queued; + __le32 ax_ofdma_brp_tried; + __le32 ax_ofdma_brp_flushed; + __le32 ax_ofdma_brp_err; + __le32 ax_ofdma_num_cbf_rcvd; +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_brp_stats_tlv { + __le32 num_elems_ax_brp_arr; + __le32 arr_elem_size_ax_brp; + DECLARE_FLEX_ARRAY(struct ath12k_htt_txbf_ofdma_ax_brp_stats_elem_t, ax_brp); +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_steer_stats_elem_t { + __le32 num_ppdu_steer; + __le32 num_ppdu_ol; + __le32 num_usr_prefetch; + __le32 num_usr_sound; + __le32 num_usr_force_sound; +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_steer_stats_tlv { + __le32 num_elems_ax_steer_arr; + __le32 arr_elem_size_ax_steer; + DECLARE_FLEX_ARRAY(struct ath12k_htt_txbf_ofdma_ax_steer_stats_elem_t, ax_steer); +} __packed; + +struct ath12k_htt_txbf_ofdma_ax_steer_mpdu_stats_tlv { + __le32 ax_ofdma_rbo_steer_mpdus_tried; + __le32 ax_ofdma_rbo_steer_mpdus_failed; + __le32 ax_ofdma_sifs_steer_mpdus_tried; + __le32 ax_ofdma_sifs_steer_mpdus_failed; +} __packed; + +enum ath12k_htt_stats_page_lock_state { + ATH12K_HTT_STATS_PAGE_LOCKED = 0, + ATH12K_HTT_STATS_PAGE_UNLOCKED = 1, + ATH12K_NUM_PG_LOCK_STATE +}; + +#define ATH12K_PAGER_MAX 10 + +#define ATH12K_HTT_DLPAGER_ASYNC_LOCK_PG_CNT_INFO0 GENMASK(7, 0) +#define ATH12K_HTT_DLPAGER_SYNC_LOCK_PG_CNT_INFO0 GENMASK(15, 8) +#define ATH12K_HTT_DLPAGER_TOTAL_LOCK_PAGES_INFO1 GENMASK(15, 0) +#define ATH12K_HTT_DLPAGER_TOTAL_FREE_PAGES_INFO1 GENMASK(31, 16) +#define ATH12K_HTT_DLPAGER_TOTAL_LOCK_PAGES_INFO2 GENMASK(15, 0) +#define ATH12K_HTT_DLPAGER_TOTAL_FREE_PAGES_INFO2 GENMASK(31, 16) + +struct ath12k_htt_pgs_info { + __le32 page_num; + __le32 num_pgs; + __le32 ts_lsb; + __le32 ts_msb; +} __packed; + +struct ath12k_htt_dl_pager_stats_tlv { + __le32 info0; + __le32 info1; + __le32 info2; + struct ath12k_htt_pgs_info pgs_info[ATH12K_NUM_PG_LOCK_STATE][ATH12K_PAGER_MAX]; +} __packed; + +#define ATH12K_HTT_STATS_MAX_CHAINS 8 +#define ATH12K_HTT_MAX_RX_PKT_CNT 8 +#define ATH12K_HTT_MAX_RX_PKT_CRC_PASS_CNT 8 +#define ATH12K_HTT_MAX_PER_BLK_ERR_CNT 20 +#define ATH12K_HTT_MAX_RX_OTA_ERR_CNT 14 +#define ATH12K_HTT_MAX_CH_PWR_INFO_SIZE 16 + +struct ath12k_htt_phy_stats_tlv { + a_sle32 nf_chain[ATH12K_HTT_STATS_MAX_CHAINS]; + __le32 false_radar_cnt; + __le32 radar_cs_cnt; + a_sle32 ani_level; + __le32 fw_run_time; + a_sle32 runtime_nf_chain[ATH12K_HTT_STATS_MAX_CHAINS]; +} __packed; + +struct ath12k_htt_phy_counters_tlv { + __le32 rx_ofdma_timing_err_cnt; + __le32 rx_cck_fail_cnt; + __le32 mactx_abort_cnt; + __le32 macrx_abort_cnt; + __le32 phytx_abort_cnt; + __le32 phyrx_abort_cnt; + __le32 phyrx_defer_abort_cnt; + __le32 rx_gain_adj_lstf_event_cnt; + __le32 rx_gain_adj_non_legacy_cnt; + __le32 rx_pkt_cnt[ATH12K_HTT_MAX_RX_PKT_CNT]; + __le32 rx_pkt_crc_pass_cnt[ATH12K_HTT_MAX_RX_PKT_CRC_PASS_CNT]; + __le32 per_blk_err_cnt[ATH12K_HTT_MAX_PER_BLK_ERR_CNT]; + __le32 rx_ota_err_cnt[ATH12K_HTT_MAX_RX_OTA_ERR_CNT]; +} __packed; + +struct ath12k_htt_phy_reset_stats_tlv { + __le32 pdev_id; + __le32 chan_mhz; + __le32 chan_band_center_freq1; + __le32 chan_band_center_freq2; + __le32 chan_phy_mode; + __le32 chan_flags; + __le32 chan_num; + __le32 reset_cause; + __le32 prev_reset_cause; + __le32 phy_warm_reset_src; + __le32 rx_gain_tbl_mode; + __le32 xbar_val; + __le32 force_calibration; + __le32 phyrf_mode; + __le32 phy_homechan; + __le32 phy_tx_ch_mask; + __le32 phy_rx_ch_mask; + __le32 phybb_ini_mask; + __le32 phyrf_ini_mask; + __le32 phy_dfs_en_mask; + __le32 phy_sscan_en_mask; + __le32 phy_synth_sel_mask; + __le32 phy_adfs_freq; + __le32 cck_fir_settings; + __le32 phy_dyn_pri_chan; + __le32 cca_thresh; + __le32 dyn_cca_status; + __le32 rxdesense_thresh_hw; + __le32 rxdesense_thresh_sw; +} __packed; + +struct ath12k_htt_phy_reset_counters_tlv { + __le32 pdev_id; + __le32 cf_active_low_fail_cnt; + __le32 cf_active_low_pass_cnt; + __le32 phy_off_through_vreg_cnt; + __le32 force_calibration_cnt; + __le32 rf_mode_switch_phy_off_cnt; + __le32 temperature_recal_cnt; +} __packed; + +struct ath12k_htt_phy_tpc_stats_tlv { + __le32 pdev_id; + __le32 tx_power_scale; + __le32 tx_power_scale_db; + __le32 min_negative_tx_power; + __le32 reg_ctl_domain; + __le32 max_reg_allowed_power[ATH12K_HTT_STATS_MAX_CHAINS]; + __le32 max_reg_allowed_power_6ghz[ATH12K_HTT_STATS_MAX_CHAINS]; + __le32 twice_max_rd_power; + __le32 max_tx_power; + __le32 home_max_tx_power; + __le32 psd_power; + __le32 eirp_power; + __le32 power_type_6ghz; + __le32 sub_band_cfreq[ATH12K_HTT_MAX_CH_PWR_INFO_SIZE]; + __le32 sub_band_txpower[ATH12K_HTT_MAX_CH_PWR_INFO_SIZE]; +} __packed; + +struct ath12k_htt_t2h_soc_txrx_stats_common_tlv { + __le32 inv_peers_msdu_drop_count_hi; + __le32 inv_peers_msdu_drop_count_lo; +} __packed; + +#define ATH12K_HTT_AST_PDEV_ID_INFO GENMASK(1, 0) +#define ATH12K_HTT_AST_VDEV_ID_INFO GENMASK(9, 2) +#define ATH12K_HTT_AST_NEXT_HOP_INFO BIT(10) +#define ATH12K_HTT_AST_MCAST_INFO BIT(11) +#define ATH12K_HTT_AST_MONITOR_DIRECT_INFO BIT(12) +#define ATH12K_HTT_AST_MESH_STA_INFO BIT(13) +#define ATH12K_HTT_AST_MEC_INFO BIT(14) +#define ATH12K_HTT_AST_INTRA_BSS_INFO BIT(15) + +struct ath12k_htt_ast_entry_tlv { + __le32 sw_peer_id; + __le32 ast_index; + struct htt_mac_addr mac_addr; + __le32 info; +} __packed; + +enum ath12k_htt_stats_direction { + ATH12K_HTT_STATS_DIRECTION_TX, + ATH12K_HTT_STATS_DIRECTION_RX +}; + +enum ath12k_htt_stats_ppdu_type { + ATH12K_HTT_STATS_PPDU_TYPE_MODE_SU, + ATH12K_HTT_STATS_PPDU_TYPE_DL_MU_MIMO, + ATH12K_HTT_STATS_PPDU_TYPE_UL_MU_MIMO, + ATH12K_HTT_STATS_PPDU_TYPE_DL_MU_OFDMA, + ATH12K_HTT_STATS_PPDU_TYPE_UL_MU_OFDMA +}; + +enum ath12k_htt_stats_param_type { + ATH12K_HTT_STATS_PREAM_OFDM, + ATH12K_HTT_STATS_PREAM_CCK, + ATH12K_HTT_STATS_PREAM_HT, + ATH12K_HTT_STATS_PREAM_VHT, + ATH12K_HTT_STATS_PREAM_HE, + ATH12K_HTT_STATS_PREAM_EHT, + ATH12K_HTT_STATS_PREAM_RSVD1, + ATH12K_HTT_STATS_PREAM_COUNT, +}; + +#define ATH12K_HTT_PUNCT_STATS_MAX_SUBBAND_CNT 32 + +struct ath12k_htt_pdev_puncture_stats_tlv { + __le32 mac_id__word; + __le32 direction; + __le32 preamble; + __le32 ppdu_type; + __le32 subband_cnt; + __le32 last_used_pattern_mask; + __le32 num_subbands_used_cnt[ATH12K_HTT_PUNCT_STATS_MAX_SUBBAND_CNT]; +} __packed; + +struct ath12k_htt_dmac_reset_stats_tlv { + __le32 reset_count; + __le32 reset_time_lo_ms; + __le32 reset_time_hi_ms; + __le32 disengage_time_lo_ms; + __le32 disengage_time_hi_ms; + __le32 engage_time_lo_ms; + __le32 engage_time_hi_ms; + __le32 disengage_count; + __le32 engage_count; + __le32 drain_dest_ring_mask; +} __packed; + +struct ath12k_htt_pdev_sched_algo_ofdma_stats_tlv { + __le32 mac_id__word; + __le32 rate_based_dlofdma_enabled_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 rate_based_dlofdma_disabled_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 rate_based_dlofdma_probing_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 rate_based_dlofdma_monitor_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 chan_acc_lat_based_dlofdma_enabled_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 chan_acc_lat_based_dlofdma_disabled_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 chan_acc_lat_based_dlofdma_monitor_cnt[ATH12K_HTT_NUM_AC_WMM]; + __le32 downgrade_to_dl_su_ru_alloc_fail[ATH12K_HTT_NUM_AC_WMM]; + __le32 candidate_list_single_user_disable_ofdma[ATH12K_HTT_NUM_AC_WMM]; + __le32 dl_cand_list_dropped_high_ul_qos_weight[ATH12K_HTT_NUM_AC_WMM]; + __le32 ax_dlofdma_disabled_due_to_pipelining[ATH12K_HTT_NUM_AC_WMM]; + __le32 dlofdma_disabled_su_only_eligible[ATH12K_HTT_NUM_AC_WMM]; + __le32 dlofdma_disabled_consec_no_mpdus_tried[ATH12K_HTT_NUM_AC_WMM]; + __le32 dlofdma_disabled_consec_no_mpdus_success[ATH12K_HTT_NUM_AC_WMM]; +} __packed; + +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_CNTRS 4 +#define ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_TXBF_RATE_STAT_NUM_MCS_CNTRS 14 + +enum ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE { + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_26, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_52, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_52_26, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_106, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_106_26, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_242, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_484, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_484_242, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996_484, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996_484_242, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x2, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x2_484, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x3, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x3_484, + ATH12K_HTT_TX_RX_PDEV_STATS_BE_RU_SIZE_996x4, + ATH12K_HTT_TX_RX_PDEV_NUM_BE_RU_SIZE_CNTRS, +}; + +enum ATH12K_HTT_RC_MODE { + ATH12K_HTT_RC_MODE_SU_OL, + ATH12K_HTT_RC_MODE_SU_BF, + ATH12K_HTT_RC_MODE_MU1_INTF, + ATH12K_HTT_RC_MODE_MU2_INTF, + ATH12K_HTT_RC_MODE_MU3_INTF, + ATH12K_HTT_RC_MODE_MU4_INTF, + ATH12K_HTT_RC_MODE_MU5_INTF, + ATH12K_HTT_RC_MODE_MU6_INTF, + ATH12K_HTT_RC_MODE_MU7_INTF, + ATH12K_HTT_RC_MODE_2D_COUNT +}; + +enum ath12k_htt_stats_rc_mode { + ATH12K_HTT_STATS_RC_MODE_DLSU = 0, + ATH12K_HTT_STATS_RC_MODE_DLMUMIMO = 1, + ATH12K_HTT_STATS_RC_MODE_DLOFDMA = 2, + ATH12K_HTT_STATS_RC_MODE_ULMUMIMO = 3, + ATH12K_HTT_STATS_RC_MODE_ULOFDMA = 4, +}; + +enum ath12k_htt_stats_ru_type { + ATH12K_HTT_STATS_RU_TYPE_INVALID, + ATH12K_HTT_STATS_RU_TYPE_SINGLE_RU_ONLY, + ATH12K_HTT_STATS_RU_TYPE_SINGLE_AND_MULTI_RU, +}; + +struct ath12k_htt_tx_rate_stats { + __le32 ppdus_tried; + __le32 ppdus_ack_failed; + __le32 mpdus_tried; + __le32 mpdus_failed; +} __packed; + +struct ath12k_htt_tx_per_rate_stats_tlv { + __le32 rc_mode; + __le32 last_probed_mcs; + __le32 last_probed_nss; + __le32 last_probed_bw; + struct ath12k_htt_tx_rate_stats per_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_CNTRS]; + struct ath12k_htt_tx_rate_stats per_nss[ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS]; + struct ath12k_htt_tx_rate_stats per_mcs[ATH12K_HTT_TXBF_RATE_STAT_NUM_MCS_CNTRS]; + struct ath12k_htt_tx_rate_stats per_bw320; + __le32 probe_cnt[ATH12K_HTT_RC_MODE_2D_COUNT]; + __le32 ru_type; + struct ath12k_htt_tx_rate_stats ru[ATH12K_HTT_TX_RX_PDEV_NUM_BE_RU_SIZE_CNTRS]; +} __packed; + +#define ATH12K_HTT_TX_PDEV_NUM_BE_MCS_CNTRS 16 +#define ATH12K_HTT_TX_PDEV_NUM_BE_BW_CNTRS 5 +#define ATH12K_HTT_TX_PDEV_NUM_EHT_SIG_MCS_CNTRS 4 +#define ATH12K_HTT_TX_PDEV_NUM_GI_CNTRS 4 + +struct ath12k_htt_tx_pdev_rate_stats_be_ofdma_tlv { + __le32 mac_id__word; + __le32 be_ofdma_tx_ldpc; + __le32 be_ofdma_tx_mcs[ATH12K_HTT_TX_PDEV_NUM_BE_MCS_CNTRS]; + __le32 be_ofdma_tx_nss[ATH12K_HTT_PDEV_STAT_NUM_SPATIAL_STREAMS]; + __le32 be_ofdma_tx_bw[ATH12K_HTT_TX_PDEV_NUM_BE_BW_CNTRS]; + __le32 gi[ATH12K_HTT_TX_PDEV_NUM_GI_CNTRS][ATH12K_HTT_TX_PDEV_NUM_BE_MCS_CNTRS]; + __le32 be_ofdma_tx_ru_size[ATH12K_HTT_TX_RX_PDEV_NUM_BE_RU_SIZE_CNTRS]; + __le32 be_ofdma_eht_sig_mcs[ATH12K_HTT_TX_PDEV_NUM_EHT_SIG_MCS_CNTRS]; +} __packed; + +struct ath12k_htt_pdev_mbssid_ctrl_frame_tlv { + __le32 mac_id__word; + __le32 basic_trigger_across_bss; + __le32 basic_trigger_within_bss; + __le32 bsr_trigger_across_bss; + __le32 bsr_trigger_within_bss; + __le32 mu_rts_across_bss; + __le32 mu_rts_within_bss; + __le32 ul_mumimo_trigger_across_bss; + __le32 ul_mumimo_trigger_within_bss; +} __packed; + +#endif diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.c b/drivers/net/wireless/ath/ath12k/debugfs_sta.c new file mode 100644 index 000000000000..5bd2bf4c9dac --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/vmalloc.h> + +#include "debugfs_sta.h" +#include "core.h" +#include "peer.h" +#include "debug.h" +#include "debugfs_htt_stats.h" +#include "debugfs.h" + +static +u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size, + bool he_rates_avail, + const struct ath12k_rx_peer_rate_stats *stats) +{ + static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = { + "1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps", + "9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps", + "24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"}; + u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS; + int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0; + u32 i, len = offset, max = max_bw * max_gi * max_mcs; + bool found; + + len += scnprintf(buf + len, size - len, "\nEHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->be_mcs_count[i], + (i + 1) % 8 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nHE stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->he_mcs_count[i], + (i + 1) % 6 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nVHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->vht_mcs_count[i], + (i + 1) % 5 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->ht_mcs_count[i], + (i + 1) % 8 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nLegacy stats:\n"); + for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++) + len += scnprintf(buf + len, size - len, + "%s: %llu%s", legacy_rate_str[i], + stats->legacy_count[i], + (i + 1) % 4 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nNSS stats:\n"); + for (i = 0; i < HAL_RX_MAX_NSS; i++) + len += scnprintf(buf + len, size - len, + "%dx%d: %llu ", i + 1, i + 1, + stats->nss_count[i]); + + len += scnprintf(buf + len, size - len, + "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n", + stats->gi_count[0], + stats->gi_count[1], + stats->gi_count[2], + stats->gi_count[3]); + + len += scnprintf(buf + len, size - len, + "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n", + stats->bw_count[0], + stats->bw_count[1], + stats->bw_count[2], + stats->bw_count[3], + stats->bw_count[4]); + + for (i = 0; i < max; i++) { + found = false; + + for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { + if (stats->rx_rate[bw][gi][nss][mcs]) { + found = true; + break; + } + } + + if (!found) + goto skip_report; + + switch (bw) { + case HAL_RX_BW_20MHZ: + bw_num = 20; + break; + case HAL_RX_BW_40MHZ: + bw_num = 40; + break; + case HAL_RX_BW_80MHZ: + bw_num = 80; + break; + case HAL_RX_BW_160MHZ: + bw_num = 160; + break; + case HAL_RX_BW_320MHZ: + bw_num = 320; + break; + } + + len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ", + bw_num, gi, nss + 1, nss + 1); + + for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { + if (stats->rx_rate[bw][gi][nss][mcs]) + len += scnprintf(buf + len, size - len, + " %d:%llu", mcs, + stats->rx_rate[bw][gi][nss][mcs]); + } + +skip_report: + if (nss++ >= max_mcs - 1) { + nss = 0; + if (gi++ >= max_gi - 1) { + gi = 0; + if (bw < max_bw - 1) + bw++; + } + } + } + + len += scnprintf(buf + len, size - len, "\n"); + + return len - offset; +} + +static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_link_sta *link_sta = file->private_data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); + const int size = ATH12K_STA_RX_STATS_BUF_SIZE; + struct ath12k_hw *ah = ahsta->ahvif->ah; + struct ath12k_rx_peer_stats *rx_stats; + struct ath12k_link_sta *arsta; + u8 link_id = link_sta->link_id; + int len = 0, i, ret = 0; + bool he_rates_avail; + struct ath12k *ar; + + wiphy_lock(ah->hw->wiphy); + + if (!(BIT(link_id) & ahsta->links_map)) { + wiphy_unlock(ah->hw->wiphy); + return -ENOENT; + } + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arsta || !arsta->arvif->ar) { + wiphy_unlock(ah->hw->wiphy); + return -ENOENT; + } + + ar = arsta->arvif->ar; + + u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!buf) { + ret = -ENOENT; + goto out; + } + + spin_lock_bh(&ar->ab->base_lock); + + rx_stats = arsta->rx_stats; + if (!rx_stats) { + ret = -ENOENT; + goto unlock; + } + + len += scnprintf(buf + len, size - len, "RX peer stats:\n\n"); + len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", + rx_stats->num_msdu); + len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", + rx_stats->tcp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", + rx_stats->udp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n", + rx_stats->other_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", + rx_stats->ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", + rx_stats->non_ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", + rx_stats->stbc_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", + rx_stats->beamformed_count); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); + + he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false; + + len += scnprintf(buf + len, size - len, + "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n", + rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], + rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], + rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]); + len += scnprintf(buf + len, size - len, + "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", + rx_stats->reception_type[0], rx_stats->reception_type[1], + rx_stats->reception_type[2], rx_stats->reception_type[3]); + + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); + + len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n", + rx_stats->rx_duration); + + len += scnprintf(buf + len, size - len, + "\nDCM: %llu\nRU26: %llu\nRU52: %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n", + rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], + rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], + rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], + rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]); + + len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n"); + len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, + &rx_stats->pkt_stats); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n"); + len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, + &rx_stats->byte_stats); + +unlock: + spin_unlock_bh(&ar->ab->base_lock); + + if (len) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); +out: + wiphy_unlock(ah->hw->wiphy); + return ret; +} + +static const struct file_operations fops_rx_stats = { + .read = ath12k_dbg_sta_dump_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_link_sta *link_sta = file->private_data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); + struct ath12k_hw *ah = ahsta->ahvif->ah; + struct ath12k_rx_peer_stats *rx_stats; + struct ath12k_link_sta *arsta; + u8 link_id = link_sta->link_id; + struct ath12k *ar; + bool reset; + int ret; + + ret = kstrtobool_from_user(buf, count, &reset); + if (ret) + return ret; + + if (!reset) + return -EINVAL; + + wiphy_lock(ah->hw->wiphy); + + if (!(BIT(link_id) & ahsta->links_map)) { + ret = -ENOENT; + goto out; + } + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arsta || !arsta->arvif->ar) { + ret = -ENOENT; + goto out; + } + + ar = arsta->arvif->ar; + + spin_lock_bh(&ar->ab->base_lock); + + rx_stats = arsta->rx_stats; + if (!rx_stats) { + spin_unlock_bh(&ar->ab->base_lock); + ret = -ENOENT; + goto out; + } + + memset(rx_stats, 0, sizeof(*rx_stats)); + spin_unlock_bh(&ar->ab->base_lock); + + ret = count; +out: + wiphy_unlock(ah->hw->wiphy); + return ret; +} + +static const struct file_operations fops_reset_rx_stats = { + .write = ath12k_dbg_sta_reset_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + struct ath12k *ar; + + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id); + if (!ar) + return; + + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) { + debugfs_create_file("rx_stats", 0400, dir, link_sta, + &fops_rx_stats); + debugfs_create_file("reset_rx_stats", 0200, dir, link_sta, + &fops_reset_rx_stats); + } +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h new file mode 100644 index 000000000000..8de924f4d7d5 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _ATH12K_DEBUGFS_STA_H_ +#define _ATH12K_DEBUGFS_STA_H_ + +#include <net/mac80211.h> + +#include "core.h" + +#define ATH12K_STA_RX_STATS_BUF_SIZE (1024 * 16) + +#ifdef CONFIG_ATH12K_DEBUGFS + +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); + +#endif /* CONFIG_ATH12K_DEBUGFS */ + +#endif /* _ATH12K_DEBUGFS_STA_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index c8e1b244b69e..6317c6d4c043 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <crypto/hash.h> @@ -14,6 +14,11 @@ #include "peer.h" #include "dp_mon.h" +enum ath12k_dp_desc_type { + ATH12K_DP_TX_DESC, + ATH12K_DP_RX_DESC, +}; + static void ath12k_dp_htt_htc_tx_complete(struct ath12k_base *ab, struct sk_buff *skb) { @@ -36,6 +41,11 @@ void ath12k_dp_peer_cleanup(struct ath12k *ar, int vdev_id, const u8 *addr) return; } + if (!peer->primary_link) { + spin_unlock_bh(&ab->base_lock); + return; + } + ath12k_dp_rx_peer_tid_cleanup(ar, peer); crypto_free_shash(peer->tfm_mmic); peer->dp_setup_done = false; @@ -127,7 +137,9 @@ static int ath12k_dp_srng_find_ring_in_mask(int ring_num, const u8 *grp_mask) static int ath12k_dp_srng_calculate_msi_group(struct ath12k_base *ab, enum hal_ring_type type, int ring_num) { + const struct ath12k_hal_tcl_to_wbm_rbm_map *map; const u8 *grp_mask; + int i; switch (type) { case HAL_WBM2SW_RELEASE: @@ -135,6 +147,14 @@ static int ath12k_dp_srng_calculate_msi_group(struct ath12k_base *ab, grp_mask = &ab->hw_params->ring_mask->rx_wbm_rel[0]; ring_num = 0; } else { + map = ab->hw_params->hal_ops->tcl_to_wbm_rbm_map; + for (i = 0; i < ab->hw_params->max_tx_ring; i++) { + if (ring_num == map[i].wbm_ring_num) { + ring_num = i; + break; + } + } + grp_mask = &ab->hw_params->ring_mask->tx[0]; } break; @@ -148,6 +168,8 @@ static int ath12k_dp_srng_calculate_msi_group(struct ath12k_base *ab, grp_mask = &ab->hw_params->ring_mask->reo_status[0]; break; case HAL_RXDMA_MONITOR_STATUS: + grp_mask = &ab->hw_params->ring_mask->rx_mon_status[0]; + break; case HAL_RXDMA_MONITOR_DST: grp_mask = &ab->hw_params->ring_mask->rx_mon_dest[0]; break; @@ -254,12 +276,17 @@ int ath12k_dp_srng_setup(struct ath12k_base *ab, struct dp_srng *ring, break; case HAL_RXDMA_BUF: case HAL_RXDMA_MONITOR_BUF: - case HAL_RXDMA_MONITOR_STATUS: params.low_threshold = num_entries >> 3; params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; params.intr_batch_cntr_thres_entries = 0; params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; break; + case HAL_RXDMA_MONITOR_STATUS: + params.low_threshold = num_entries >> 3; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.intr_batch_cntr_thres_entries = 1; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; case HAL_TX_MONITOR_DST: params.low_threshold = DP_TX_MONITOR_BUF_SIZE_MAX >> 3; params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; @@ -312,27 +339,32 @@ int ath12k_dp_srng_setup(struct ath12k_base *ab, struct dp_srng *ring, } static -u32 ath12k_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_vif *arvif) +u32 ath12k_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, + struct ath12k_link_vif *arvif) { u32 bank_config = 0; + struct ath12k_vif *ahvif = arvif->ahvif; /* Only valid for raw frames with HW crypto enabled. * With SW crypto, mac80211 sets key per packet */ - if (arvif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && + if (ahvif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) bank_config |= - u32_encode_bits(ath12k_dp_tx_get_encrypt_type(arvif->key_cipher), + u32_encode_bits(ath12k_dp_tx_get_encrypt_type(ahvif->key_cipher), HAL_TX_BANK_CONFIG_ENCRYPT_TYPE); - bank_config |= u32_encode_bits(arvif->tx_encap_type, + bank_config |= u32_encode_bits(ahvif->tx_encap_type, HAL_TX_BANK_CONFIG_ENCAP_TYPE); bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_SRC_BUFFER_SWAP) | u32_encode_bits(0, HAL_TX_BANK_CONFIG_LINK_META_SWAP) | u32_encode_bits(0, HAL_TX_BANK_CONFIG_EPD); /* only valid if idx_lookup_override is not set in tcl_data_cmd */ - bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN); + if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) + bank_config |= u32_encode_bits(1, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN); + else + bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN); bank_config |= u32_encode_bits(arvif->hal_addr_search_flags & HAL_TX_ADDRX_EN, HAL_TX_BANK_CONFIG_ADDRX_EN) | @@ -340,7 +372,7 @@ u32 ath12k_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_vif HAL_TX_ADDRY_EN), HAL_TX_BANK_CONFIG_ADDRY_EN); - bank_config |= u32_encode_bits(ieee80211_vif_is_mesh(arvif->vif) ? 3 : 0, + bank_config |= u32_encode_bits(ieee80211_vif_is_mesh(ahvif->vif) ? 3 : 0, HAL_TX_BANK_CONFIG_MESH_EN) | u32_encode_bits(arvif->vdev_id_check_en, HAL_TX_BANK_CONFIG_VDEV_ID_CHECK_EN); @@ -350,7 +382,8 @@ u32 ath12k_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_vif return bank_config; } -static int ath12k_dp_tx_get_bank_profile(struct ath12k_base *ab, struct ath12k_vif *arvif, +static int ath12k_dp_tx_get_bank_profile(struct ath12k_base *ab, + struct ath12k_link_vif *arvif, struct ath12k_dp *dp) { int bank_id = DP_INVALID_BANK_ID; @@ -452,8 +485,6 @@ static void ath12k_dp_srng_common_cleanup(struct ath12k_base *ab) ath12k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); ath12k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); } - ath12k_dp_srng_cleanup(ab, &dp->tcl_status_ring); - ath12k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); ath12k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); } @@ -474,20 +505,6 @@ static int ath12k_dp_srng_common_setup(struct ath12k_base *ab) goto err; } - ret = ath12k_dp_srng_setup(ab, &dp->tcl_cmd_ring, HAL_TCL_CMD, 0, 0, - DP_TCL_CMD_RING_SIZE); - if (ret) { - ath12k_warn(ab, "failed to set up tcl_cmd ring :%d\n", ret); - goto err; - } - - ret = ath12k_dp_srng_setup(ab, &dp->tcl_status_ring, HAL_TCL_STATUS, - 0, 0, DP_TCL_STATUS_RING_SIZE); - if (ret) { - ath12k_warn(ab, "failed to set up tcl_status ring :%d\n", ret); - goto err; - } - for (i = 0; i < ab->hw_params->max_tx_ring; i++) { map = ab->hw_params->hal_ops->tcl_to_wbm_rbm_map; tx_comp_ring_num = map[i].wbm_ring_num; @@ -611,6 +628,7 @@ static int ath12k_dp_scatter_idle_link_desc_setup(struct ath12k_base *ab, int i; int ret = 0; u32 end_offset, cookie; + enum hal_rx_buf_return_buf_manager rbm = dp->idle_link_rbm; n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / ath12k_hal_srng_get_entrysize(ab, HAL_WBM_IDLE_LINK); @@ -641,7 +659,8 @@ static int ath12k_dp_scatter_idle_link_desc_setup(struct ath12k_base *ab, paddr = link_desc_banks[i].paddr; while (n_entries) { cookie = DP_LINK_DESC_COOKIE_SET(n_entries, i); - ath12k_hal_set_link_desc_addr(scatter_buf, cookie, paddr); + ath12k_hal_set_link_desc_addr(scatter_buf, cookie, + paddr, rbm); n_entries--; paddr += HAL_LINK_DESC_SIZE; if (rem_entries) { @@ -785,6 +804,7 @@ int ath12k_dp_link_desc_setup(struct ath12k_base *ab, u32 paddr; int i, ret; u32 cookie; + enum hal_rx_buf_return_buf_manager rbm = ab->dp.idle_link_rbm; tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE; tot_mem_sz += HAL_LINK_DESC_ALIGN; @@ -845,8 +865,7 @@ int ath12k_dp_link_desc_setup(struct ath12k_base *ab, while (n_entries && (desc = ath12k_hal_srng_src_get_next_entry(ab, srng))) { cookie = DP_LINK_DESC_COOKIE_SET(n_entries, i); - ath12k_hal_set_link_desc_addr(desc, - cookie, paddr); + ath12k_hal_set_link_desc_addr(desc, cookie, paddr, rbm); n_entries--; paddr += HAL_LINK_DESC_SIZE; } @@ -876,11 +895,9 @@ int ath12k_dp_service_srng(struct ath12k_base *ab, enum dp_monitor_mode monitor_mode; u8 ring_mask; - while (i < ab->hw_params->max_tx_ring) { - if (ab->hw_params->ring_mask->tx[grp_id] & - BIT(ab->hw_params->hal_ops->tcl_to_wbm_rbm_map[i].wbm_ring_num)) - ath12k_dp_tx_completion_handler(ab, i); - i++; + if (ab->hw_params->ring_mask->tx[grp_id]) { + i = fls(ab->hw_params->ring_mask->tx[grp_id]) - 1; + ath12k_dp_tx_completion_handler(ab, i); } if (ab->hw_params->ring_mask->rx_err[grp_id]) { @@ -912,12 +929,31 @@ int ath12k_dp_service_srng(struct ath12k_base *ab, goto done; } + if (ab->hw_params->ring_mask->rx_mon_status[grp_id]) { + ring_mask = ab->hw_params->ring_mask->rx_mon_status[grp_id]; + for (i = 0; i < ab->num_radios; i++) { + for (j = 0; j < ab->hw_params->num_rxdma_per_pdev; j++) { + int id = i * ab->hw_params->num_rxdma_per_pdev + j; + + if (ring_mask & BIT(id)) { + work_done = + ath12k_dp_mon_process_ring(ab, id, napi, budget, + 0); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; + } + } + } + } + if (ab->hw_params->ring_mask->rx_mon_dest[grp_id]) { monitor_mode = ATH12K_DP_RX_MONITOR_MODE; ring_mask = ab->hw_params->ring_mask->rx_mon_dest[grp_id]; for (i = 0; i < ab->num_radios; i++) { - for (j = 0; j < ab->hw_params->num_rxmda_per_pdev; j++) { - int id = i * ab->hw_params->num_rxmda_per_pdev + j; + for (j = 0; j < ab->hw_params->num_rxdma_per_pdev; j++) { + int id = i * ab->hw_params->num_rxdma_per_pdev + j; if (ring_mask & BIT(id)) { work_done = @@ -937,8 +973,8 @@ int ath12k_dp_service_srng(struct ath12k_base *ab, monitor_mode = ATH12K_DP_TX_MONITOR_MODE; ring_mask = ab->hw_params->ring_mask->tx_mon_dest[grp_id]; for (i = 0; i < ab->num_radios; i++) { - for (j = 0; j < ab->hw_params->num_rxmda_per_pdev; j++) { - int id = i * ab->hw_params->num_rxmda_per_pdev + j; + for (j = 0; j < ab->hw_params->num_rxdma_per_pdev; j++) { + int id = i * ab->hw_params->num_rxdma_per_pdev + j; if (ring_mask & BIT(id)) { work_done = @@ -960,8 +996,9 @@ int ath12k_dp_service_srng(struct ath12k_base *ab, if (ab->hw_params->ring_mask->host2rxdma[grp_id]) { struct ath12k_dp *dp = &ab->dp; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + LIST_HEAD(list); - ath12k_dp_rx_bufs_replenish(ab, rx_ring, 0); + ath12k_dp_rx_bufs_replenish(ab, rx_ring, &list, 0); } /* TODO: Implement handler for other interrupts */ @@ -974,27 +1011,18 @@ void ath12k_dp_pdev_free(struct ath12k_base *ab) { int i; - del_timer_sync(&ab->mon_reap_timer); - for (i = 0; i < ab->num_radios; i++) ath12k_dp_rx_pdev_free(ab, i); } -void ath12k_dp_pdev_pre_alloc(struct ath12k_base *ab) +void ath12k_dp_pdev_pre_alloc(struct ath12k *ar) { - struct ath12k *ar; - struct ath12k_pdev_dp *dp; - int i; - - for (i = 0; i < ab->num_radios; i++) { - ar = ab->pdevs[i].ar; - dp = &ar->dp; - dp->mac_id = i; - atomic_set(&dp->num_tx_pending, 0); - init_waitqueue_head(&dp->tx_empty_waitq); + struct ath12k_pdev_dp *dp = &ar->dp; - /* TODO: Add any RXDMA setup required per pdev */ - } + dp->mac_id = ar->pdev_idx; + atomic_set(&dp->num_tx_pending, 0); + init_waitqueue_head(&dp->tx_empty_waitq); + /* TODO: Add any RXDMA setup required per pdev */ } bool ath12k_dp_wmask_compaction_rx_tlv_supported(struct ath12k_base *ab) @@ -1020,27 +1048,6 @@ void ath12k_dp_hal_rx_desc_init(struct ath12k_base *ab) ab->hal_rx_ops->rx_desc_get_desc_size(); } -static void ath12k_dp_service_mon_ring(struct timer_list *t) -{ - struct ath12k_base *ab = from_timer(ab, t, mon_reap_timer); - int i; - - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) - ath12k_dp_mon_process_ring(ab, i, NULL, DP_MON_SERVICE_BUDGET, - ATH12K_DP_RX_MONITOR_MODE); - - mod_timer(&ab->mon_reap_timer, jiffies + - msecs_to_jiffies(ATH12K_MON_TIMER_INTERVAL)); -} - -static void ath12k_dp_mon_reap_timer_init(struct ath12k_base *ab) -{ - if (ab->hw_params->rxdma1_enable) - return; - - timer_setup(&ab->mon_reap_timer, ath12k_dp_service_mon_ring, 0); -} - int ath12k_dp_pdev_alloc(struct ath12k_base *ab) { struct ath12k *ar; @@ -1051,8 +1058,6 @@ int ath12k_dp_pdev_alloc(struct ath12k_base *ab) if (ret) goto out; - ath12k_dp_mon_reap_timer_init(ab); - /* TODO: Per-pdev rx ring unlike tx ring which is mapped to different AC's */ for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -1099,15 +1104,12 @@ int ath12k_dp_htt_connect(struct ath12k_dp *dp) return 0; } -static void ath12k_dp_update_vdev_search(struct ath12k_vif *arvif) +static void ath12k_dp_update_vdev_search(struct ath12k_link_vif *arvif) { - switch (arvif->vdev_type) { + switch (arvif->ahvif->vdev_type) { case WMI_VDEV_TYPE_STA: - /* TODO: Verify the search type and flags since ast hash - * is not part of peer mapv3 - */ arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; - arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; break; case WMI_VDEV_TYPE_AP: case WMI_VDEV_TYPE_IBSS: @@ -1120,7 +1122,7 @@ static void ath12k_dp_update_vdev_search(struct ath12k_vif *arvif) } } -void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_vif *arvif) +void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) { struct ath12k_base *ab = ar->ab; @@ -1146,11 +1148,13 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_vif *arvif) static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) { - struct ath12k_rx_desc_info *desc_info, *tmp; + struct ath12k_rx_desc_info *desc_info; struct ath12k_tx_desc_info *tx_desc_info, *tmp1; struct ath12k_dp *dp = &ab->dp; + struct ath12k_skb_cb *skb_cb; struct sk_buff *skb; - int i; + struct ath12k *ar; + int i, j; u32 pool_id, tx_spt_page; if (!dp->spt_info) @@ -1159,24 +1163,31 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) /* RX Descriptor cleanup */ spin_lock_bh(&dp->rx_desc_lock); - list_for_each_entry_safe(desc_info, tmp, &dp->rx_desc_used_list, list) { - list_del(&desc_info->list); - skb = desc_info->skb; + for (i = 0; i < ATH12K_NUM_RX_SPT_PAGES; i++) { + desc_info = dp->rxbaddr[i]; - if (!skb) - continue; + for (j = 0; j < ATH12K_MAX_SPT_ENTRIES; j++) { + if (!desc_info[j].in_use) { + list_del(&desc_info[j].list); + continue; + } - dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr, - skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); + skb = desc_info[j].skb; + if (!skb) + continue; + + dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } } for (i = 0; i < ATH12K_NUM_RX_SPT_PAGES; i++) { - if (!dp->spt_info->rxbaddr[i]) + if (!dp->rxbaddr[i]) continue; - kfree(dp->spt_info->rxbaddr[i]); - dp->spt_info->rxbaddr[i] = NULL; + kfree(dp->rxbaddr[i]); + dp->rxbaddr[i] = NULL; } spin_unlock_bh(&dp->rx_desc_lock); @@ -1193,6 +1204,25 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) if (!skb) continue; + skb_cb = ATH12K_SKB_CB(skb); + if (skb_cb->paddr_ext_desc) { + dma_unmap_single(ab->dev, + skb_cb->paddr_ext_desc, + tx_desc_info->skb_ext_desc->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(tx_desc_info->skb_ext_desc); + } + + /* if we are unregistering, hw would've been destroyed and + * ar is no longer valid. + */ + if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) { + ar = skb_cb->ar; + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + } + dma_unmap_single(ab->dev, ATH12K_SKB_CB(skb)->paddr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); @@ -1206,11 +1236,11 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) for (i = 0; i < ATH12K_TX_SPT_PAGES_PER_POOL; i++) { tx_spt_page = i + pool_id * ATH12K_TX_SPT_PAGES_PER_POOL; - if (!dp->spt_info->txbaddr[tx_spt_page]) + if (!dp->txbaddr[tx_spt_page]) continue; - kfree(dp->spt_info->txbaddr[tx_spt_page]); - dp->spt_info->txbaddr[tx_spt_page] = NULL; + kfree(dp->txbaddr[tx_spt_page]); + dp->txbaddr[tx_spt_page] = NULL; } spin_unlock_bh(&dp->tx_desc_lock[pool_id]); @@ -1227,6 +1257,7 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) } kfree(dp->spt_info); + dp->spt_info = NULL; } static void ath12k_dp_reoq_lut_cleanup(struct ath12k_base *ab) @@ -1236,15 +1267,25 @@ static void ath12k_dp_reoq_lut_cleanup(struct ath12k_base *ab) if (!ab->hw_params->reoq_lut_support) return; - if (!dp->reoq_lut.vaddr) - return; - - dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE, - dp->reoq_lut.vaddr, dp->reoq_lut.paddr); - dp->reoq_lut.vaddr = NULL; - - ath12k_hif_write32(ab, - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE0(ab), 0); + if (dp->reoq_lut.vaddr_unaligned) { + ath12k_hif_write32(ab, + HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_LUT_BASE0(ab), 0); + dma_free_coherent(ab->dev, dp->reoq_lut.size, + dp->reoq_lut.vaddr_unaligned, + dp->reoq_lut.paddr_unaligned); + dp->reoq_lut.vaddr_unaligned = NULL; + } + + if (dp->ml_reoq_lut.vaddr_unaligned) { + ath12k_hif_write32(ab, + HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_LUT_BASE1(ab), 0); + dma_free_coherent(ab->dev, dp->ml_reoq_lut.size, + dp->ml_reoq_lut.vaddr_unaligned, + dp->ml_reoq_lut.paddr_unaligned); + dp->ml_reoq_lut.vaddr_unaligned = NULL; + } } void ath12k_dp_free(struct ath12k_base *ab) @@ -1252,6 +1293,9 @@ void ath12k_dp_free(struct ath12k_base *ab) struct ath12k_dp *dp = &ab->dp; int i; + if (!dp->ab) + return; + ath12k_dp_link_desc_cleanup(ab, dp->link_desc_banks, HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); @@ -1262,11 +1306,14 @@ void ath12k_dp_free(struct ath12k_base *ab) ath12k_dp_rx_reo_cmd_list_cleanup(ab); - for (i = 0; i < ab->hw_params->max_tx_ring; i++) + for (i = 0; i < ab->hw_params->max_tx_ring; i++) { kfree(dp->tx_ring[i].tx_status); + dp->tx_ring[i].tx_status = NULL; + } ath12k_dp_rx_free(ab); /* Deinit any SOC level resource */ + dp->ab = NULL; } void ath12k_dp_cc_config(struct ath12k_base *ab) @@ -1276,6 +1323,9 @@ void ath12k_dp_cc_config(struct ath12k_base *ab) u32 wbm_base = HAL_SEQ_WCSS_UMAC_WBM_REG; u32 val = 0; + if (ath12k_ftm_mode) + return; + ath12k_hif_write32(ab, reo_base + HAL_REO1_SW_COOKIE_CFG0(ab), cmem_base); val |= u32_encode_bits(ATH12K_CMEM_ADDR_MSB, @@ -1335,16 +1385,22 @@ static inline void *ath12k_dp_cc_get_desc_addr_ptr(struct ath12k_base *ab, struct ath12k_rx_desc_info *ath12k_dp_get_rx_desc(struct ath12k_base *ab, u32 cookie) { + struct ath12k_dp *dp = &ab->dp; struct ath12k_rx_desc_info **desc_addr_ptr; - u16 ppt_idx, spt_idx; + u16 start_ppt_idx, end_ppt_idx, ppt_idx, spt_idx; ppt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_PPT); - spt_idx = u32_get_bits(cookie, ATH12k_DP_CC_COOKIE_SPT); + spt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_SPT); + + start_ppt_idx = dp->rx_ppt_base + ATH12K_RX_SPT_PAGE_OFFSET; + end_ppt_idx = start_ppt_idx + ATH12K_NUM_RX_SPT_PAGES; - if (ppt_idx > ATH12K_NUM_RX_SPT_PAGES || + if (ppt_idx < start_ppt_idx || + ppt_idx >= end_ppt_idx || spt_idx > ATH12K_MAX_SPT_ENTRIES) return NULL; + ppt_idx = ppt_idx - dp->rx_ppt_base; desc_addr_ptr = ath12k_dp_cc_get_desc_addr_ptr(ab, ppt_idx, spt_idx); return *desc_addr_ptr; @@ -1354,13 +1410,17 @@ struct ath12k_tx_desc_info *ath12k_dp_get_tx_desc(struct ath12k_base *ab, u32 cookie) { struct ath12k_tx_desc_info **desc_addr_ptr; - u16 ppt_idx, spt_idx; + u16 start_ppt_idx, end_ppt_idx, ppt_idx, spt_idx; ppt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_PPT); - spt_idx = u32_get_bits(cookie, ATH12k_DP_CC_COOKIE_SPT); + spt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_SPT); + + start_ppt_idx = ATH12K_TX_SPT_PAGE_OFFSET; + end_ppt_idx = start_ppt_idx + + (ATH12K_TX_SPT_PAGES_PER_POOL * ATH12K_HW_MAX_QUEUES); - if (ppt_idx < ATH12K_NUM_RX_SPT_PAGES || - ppt_idx > ab->dp.num_spt_pages || + if (ppt_idx < start_ppt_idx || + ppt_idx >= end_ppt_idx || spt_idx > ATH12K_MAX_SPT_ENTRIES) return NULL; @@ -1375,7 +1435,7 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) struct ath12k_rx_desc_info *rx_descs, **rx_desc_addr; struct ath12k_tx_desc_info *tx_descs, **tx_desc_addr; u32 i, j, pool_id, tx_spt_page; - u32 ppt_idx; + u32 ppt_idx, cookie_ppt_idx; spin_lock_bh(&dp->rx_desc_lock); @@ -1389,15 +1449,18 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) return -ENOMEM; } - dp->spt_info->rxbaddr[i] = &rx_descs[0]; + ppt_idx = ATH12K_RX_SPT_PAGE_OFFSET + i; + cookie_ppt_idx = dp->rx_ppt_base + ppt_idx; + dp->rxbaddr[i] = &rx_descs[0]; for (j = 0; j < ATH12K_MAX_SPT_ENTRIES; j++) { - rx_descs[j].cookie = ath12k_dp_cc_cookie_gen(i, j); + rx_descs[j].cookie = ath12k_dp_cc_cookie_gen(cookie_ppt_idx, j); rx_descs[j].magic = ATH12K_DP_RX_DESC_MAGIC; + rx_descs[j].device_id = ab->device_id; list_add_tail(&rx_descs[j].list, &dp->rx_desc_free_list); /* Update descriptor VA in SPT */ - rx_desc_addr = ath12k_dp_cc_get_desc_addr_ptr(ab, i, j); + rx_desc_addr = ath12k_dp_cc_get_desc_addr_ptr(ab, ppt_idx, j); *rx_desc_addr = &rx_descs[j]; } } @@ -1417,10 +1480,11 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) } tx_spt_page = i + pool_id * ATH12K_TX_SPT_PAGES_PER_POOL; - dp->spt_info->txbaddr[tx_spt_page] = &tx_descs[0]; + ppt_idx = ATH12K_TX_SPT_PAGE_OFFSET + tx_spt_page; + + dp->txbaddr[tx_spt_page] = &tx_descs[0]; for (j = 0; j < ATH12K_MAX_SPT_ENTRIES; j++) { - ppt_idx = ATH12K_NUM_RX_SPT_PAGES + tx_spt_page; tx_descs[j].desc_id = ath12k_dp_cc_cookie_gen(ppt_idx, j); tx_descs[j].pool_id = pool_id; list_add_tail(&tx_descs[j].list, @@ -1437,14 +1501,57 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) return 0; } +static int ath12k_dp_cmem_init(struct ath12k_base *ab, + struct ath12k_dp *dp, + enum ath12k_dp_desc_type type) +{ + u32 cmem_base; + int i, start, end; + + cmem_base = ab->qmi.dev_mem[ATH12K_QMI_DEVMEM_CMEM_INDEX].start; + + switch (type) { + case ATH12K_DP_TX_DESC: + start = ATH12K_TX_SPT_PAGE_OFFSET; + end = start + ATH12K_NUM_TX_SPT_PAGES; + break; + case ATH12K_DP_RX_DESC: + cmem_base += ATH12K_PPT_ADDR_OFFSET(dp->rx_ppt_base); + start = ATH12K_RX_SPT_PAGE_OFFSET; + end = start + ATH12K_NUM_RX_SPT_PAGES; + break; + default: + ath12k_err(ab, "invalid descriptor type %d in cmem init\n", type); + return -EINVAL; + } + + /* Write to PPT in CMEM */ + for (i = start; i < end; i++) + ath12k_hif_write32(ab, cmem_base + ATH12K_PPT_ADDR_OFFSET(i), + dp->spt_info[i].paddr >> ATH12K_SPT_4K_ALIGN_OFFSET); + + return 0; +} + +void ath12k_dp_partner_cc_init(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + int i; + + for (i = 0; i < ag->num_devices; i++) { + if (ag->ab[i] == ab) + continue; + + ath12k_dp_cmem_init(ab, &ag->ab[i]->dp, ATH12K_DP_RX_DESC); + } +} + static int ath12k_dp_cc_init(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; int i, ret = 0; - u32 cmem_base; INIT_LIST_HEAD(&dp->rx_desc_free_list); - INIT_LIST_HEAD(&dp->rx_desc_used_list); spin_lock_init(&dp->rx_desc_lock); for (i = 0; i < ATH12K_HW_MAX_QUEUES; i++) { @@ -1465,7 +1572,7 @@ static int ath12k_dp_cc_init(struct ath12k_base *ab) return -ENOMEM; } - cmem_base = ab->qmi.dev_mem[ATH12K_QMI_DEVMEM_CMEM_INDEX].start; + dp->rx_ppt_base = ab->device_id * ATH12K_NUM_RX_SPT_PAGES; for (i = 0; i < dp->num_spt_pages; i++) { dp->spt_info[i].vaddr = dma_alloc_coherent(ab->dev, @@ -1483,10 +1590,18 @@ static int ath12k_dp_cc_init(struct ath12k_base *ab) ret = -EINVAL; goto free; } + } - /* Write to PPT in CMEM */ - ath12k_hif_write32(ab, cmem_base + ATH12K_PPT_ADDR_OFFSET(i), - dp->spt_info[i].paddr >> ATH12K_SPT_4K_ALIGN_OFFSET); + ret = ath12k_dp_cmem_init(ab, dp, ATH12K_DP_TX_DESC); + if (ret) { + ath12k_warn(ab, "HW CC Tx cmem init failed %d", ret); + goto free; + } + + ret = ath12k_dp_cmem_init(ab, dp, ATH12K_DP_RX_DESC); + if (ret) { + ath12k_warn(ab, "HW CC Rx cmem init failed %d", ret); + goto free; } ret = ath12k_dp_cc_desc_init(ab); @@ -1501,27 +1616,88 @@ free: return ret; } +static int ath12k_dp_alloc_reoq_lut(struct ath12k_base *ab, + struct ath12k_reo_q_addr_lut *lut) +{ + lut->size = DP_REOQ_LUT_SIZE + HAL_REO_QLUT_ADDR_ALIGN - 1; + lut->vaddr_unaligned = dma_alloc_coherent(ab->dev, lut->size, + &lut->paddr_unaligned, + GFP_KERNEL | __GFP_ZERO); + if (!lut->vaddr_unaligned) + return -ENOMEM; + + lut->vaddr = PTR_ALIGN(lut->vaddr_unaligned, HAL_REO_QLUT_ADDR_ALIGN); + lut->paddr = lut->paddr_unaligned + + ((unsigned long)lut->vaddr - (unsigned long)lut->vaddr_unaligned); + return 0; +} + static int ath12k_dp_reoq_lut_setup(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + u32 val; + int ret; if (!ab->hw_params->reoq_lut_support) return 0; - dp->reoq_lut.vaddr = dma_alloc_coherent(ab->dev, - DP_REOQ_LUT_SIZE, - &dp->reoq_lut.paddr, - GFP_KERNEL | __GFP_ZERO); - if (!dp->reoq_lut.vaddr) { + ret = ath12k_dp_alloc_reoq_lut(ab, &dp->reoq_lut); + if (ret) { ath12k_warn(ab, "failed to allocate memory for reoq table"); - return -ENOMEM; + return ret; + } + + ret = ath12k_dp_alloc_reoq_lut(ab, &dp->ml_reoq_lut); + if (ret) { + ath12k_warn(ab, "failed to allocate memory for ML reoq table"); + dma_free_coherent(ab->dev, dp->reoq_lut.size, + dp->reoq_lut.vaddr_unaligned, + dp->reoq_lut.paddr_unaligned); + dp->reoq_lut.vaddr_unaligned = NULL; + return ret; } + /* Bits in the register have address [39:8] LUT base address to be + * allocated such that LSBs are assumed to be zero. Also, current + * design supports paddr up to 4 GB max hence it fits in 32 bit + * register only + */ + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE0(ab), - dp->reoq_lut.paddr); + dp->reoq_lut.paddr >> 8); + + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE1(ab), + dp->ml_reoq_lut.paddr >> 8); + + val = ath12k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_ADDR(ab)); + + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_ADDR(ab), + val | HAL_REO_QDESC_ADDR_READ_LUT_ENABLE); + + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_MAX_PEERID(ab), + HAL_REO_QDESC_MAX_PEERID); + return 0; } +static enum hal_rx_buf_return_buf_manager +ath12k_dp_get_idle_link_rbm(struct ath12k_base *ab) +{ + switch (ab->device_id) { + case 0: + return HAL_RX_BUF_RBM_WBM_DEV0_IDLE_DESC_LIST; + case 1: + return HAL_RX_BUF_RBM_WBM_DEV1_IDLE_DESC_LIST; + case 2: + return HAL_RX_BUF_RBM_WBM_DEV2_IDLE_DESC_LIST; + default: + ath12k_warn(ab, "invalid %d device id, so choose default rbm\n", + ab->device_id); + WARN_ON(1); + return HAL_RX_BUF_RBM_WBM_DEV0_IDLE_DESC_LIST; + } +} + int ath12k_dp_alloc(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; @@ -1538,6 +1714,7 @@ int ath12k_dp_alloc(struct ath12k_base *ab) spin_lock_init(&dp->reo_cmd_lock); dp->reo_cmd_cache_flush_count = 0; + dp->idle_link_rbm = ath12k_dp_get_idle_link_rbm(ab); ret = ath12k_wbm_idle_ring_setup(ab, &n_link_desc); if (ret) { diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index eb2dd408e081..a353333f83b6 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_H #define ATH12K_DP_H +#include "hal_desc.h" #include "hal_rx.h" #include "hw.h" @@ -16,6 +17,7 @@ struct ath12k_base; struct ath12k_peer; struct ath12k_dp; struct ath12k_vif; +struct ath12k_link_vif; struct hal_tcl_status_ring; struct ath12k_ext_irq_grp; @@ -68,6 +70,16 @@ struct ath12k_pdev_mon_stats { u32 dest_mpdu_drop; u32 dup_mon_linkdesc_cnt; u32 dup_mon_buf_cnt; + u32 dest_mon_stuck; + u32 dest_mon_not_reaped; +}; + +enum dp_mon_status_buf_state { + DP_MON_STATUS_MATCH, + DP_MON_STATUS_NO_DMA, + DP_MON_STATUS_LAG, + DP_MON_STATUS_LEAD, + DP_MON_STATUS_REPLINISH, }; struct dp_link_desc_bank { @@ -105,6 +117,8 @@ struct dp_mon_mpdu { struct list_head list; struct sk_buff *head; struct sk_buff *tail; + u32 err_bitmap; + u8 decap_format; }; #define DP_MON_MAX_STATUS_BUF 32 @@ -117,14 +131,16 @@ struct ath12k_mon_data { u32 mon_last_buf_cookie; u64 mon_last_linkdesc_paddr; u16 chan_noise_floor; + u32 err_bitmap; + u8 decap_format; struct ath12k_pdev_mon_stats rx_mon_stats; + enum dp_mon_status_buf_state buf_state; /* lock for monitor data */ spinlock_t mon_lock; struct sk_buff_head rx_status_q; struct dp_mon_mpdu *mon_mpdu; struct list_head dp_rx_mon_mpdu_list; - struct sk_buff *dest_skb_q[DP_MON_MAX_STATUS_BUF]; struct dp_mon_tx_ppdu_info *tx_prot_ppdu_info; struct dp_mon_tx_ppdu_info *tx_data_ppdu_info; }; @@ -175,7 +191,7 @@ struct ath12k_pdev_dp { #define DP_RXDMA_ERR_DST_RING_SIZE 1024 #define DP_RXDMA_MON_STATUS_RING_SIZE 1024 #define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 -#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 8092 #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 #define DP_TX_MONITOR_BUF_RING_SIZE 4096 #define DP_TX_MONITOR_DEST_RING_SIZE 2048 @@ -188,6 +204,14 @@ struct ath12k_pdev_dp { #define DP_RX_BUFFER_SIZE_LITE 1024 #define DP_RX_BUFFER_ALIGN_SIZE 128 +#define RX_MON_STATUS_BASE_BUF_SIZE 2048 +#define RX_MON_STATUS_BUF_ALIGN 128 +#define RX_MON_STATUS_BUF_RESERVATION 128 +#define RX_MON_STATUS_BUF_SIZE (RX_MON_STATUS_BASE_BUF_SIZE - \ + (RX_MON_STATUS_BUF_RESERVATION + \ + RX_MON_STATUS_BUF_ALIGN + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))) + #define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) #define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(19, 18) @@ -223,6 +247,9 @@ struct ath12k_pdev_dp { #define ATH12K_NUM_TX_SPT_PAGES (ATH12K_TX_SPT_PAGES_PER_POOL * ATH12K_HW_MAX_QUEUES) #define ATH12K_NUM_SPT_PAGES (ATH12K_NUM_RX_SPT_PAGES + ATH12K_NUM_TX_SPT_PAGES) +#define ATH12K_TX_SPT_PAGE_OFFSET 0 +#define ATH12K_RX_SPT_PAGE_OFFSET ATH12K_NUM_TX_SPT_PAGES + /* The SPT pages are divided for RX and TX, first block for RX * and remaining for TX */ @@ -245,7 +272,7 @@ struct ath12k_pdev_dp { #define ATH12K_CC_SPT_MSB 8 #define ATH12K_CC_PPT_MSB 19 #define ATH12K_CC_PPT_SHIFT 9 -#define ATH12k_DP_CC_COOKIE_SPT GENMASK(8, 0) +#define ATH12K_DP_CC_COOKIE_SPT GENMASK(8, 0) #define ATH12K_DP_CC_COOKIE_PPT GENMASK(19, 9) #define DP_REO_QREF_NUM GENMASK(31, 16) @@ -260,6 +287,9 @@ struct ath12k_pdev_dp { /* Invalid TX Bank ID value */ #define DP_INVALID_BANK_ID -1 +#define MAX_TQM_RELEASE_REASON 15 +#define MAX_FW_TX_STATUS 7 + struct ath12k_dp_tx_bank_profile { u8 is_configured; u32 num_users; @@ -282,21 +312,29 @@ struct ath12k_rx_desc_info { struct sk_buff *skb; u32 cookie; u32 magic; + u8 in_use : 1, + device_id : 3, + reserved : 4; }; struct ath12k_tx_desc_info { struct list_head list; struct sk_buff *skb; + struct sk_buff *skb_ext_desc; u32 desc_id; /* Cookie */ u8 mac_id; u8 pool_id; }; +struct ath12k_tx_desc_params { + struct sk_buff *skb; + struct sk_buff *skb_ext_desc; + u8 mac_id; +}; + struct ath12k_spt_info { dma_addr_t paddr; u64 *vaddr; - struct ath12k_rx_desc_info *rxbaddr[ATH12K_NUM_RX_SPT_PAGES]; - struct ath12k_tx_desc_info *txbaddr[ATH12K_NUM_TX_SPT_PAGES]; }; struct ath12k_reo_queue_ref { @@ -305,12 +343,26 @@ struct ath12k_reo_queue_ref { } __packed; struct ath12k_reo_q_addr_lut { - dma_addr_t paddr; + u32 *vaddr_unaligned; u32 *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + u32 size; +}; + +struct ath12k_link_stats { + u32 tx_enqueued; + u32 tx_completed; + u32 tx_bcast_mcast; + u32 tx_dropped; + u32 tx_encap_type[HAL_TCL_ENCAP_TYPE_MAX]; + u32 tx_encrypt_type[HAL_ENCRYPT_TYPE_MAX]; + u32 tx_desc_type[HAL_TCL_DESC_TYPE_MAX]; }; struct ath12k_dp { struct ath12k_base *ab; + u32 mon_dest_ring_stuck_cnt; u8 num_bank_profiles; /* protects the access and update of bank_profiles */ spinlock_t tx_bank_lock; @@ -320,15 +372,15 @@ struct ath12k_dp { u8 htt_tgt_ver_major; u8 htt_tgt_ver_minor; struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + enum hal_rx_buf_return_buf_manager idle_link_rbm; struct dp_srng wbm_idle_ring; struct dp_srng wbm_desc_rel_ring; - struct dp_srng tcl_cmd_ring; - struct dp_srng tcl_status_ring; struct dp_srng reo_reinject_ring; struct dp_srng rx_rel_ring; struct dp_srng reo_except_ring; struct dp_srng reo_cmd_ring; struct dp_srng reo_status_ring; + enum ath12k_peer_metadata_version peer_metadata_ver; struct dp_srng reo_dst_ring[DP_REO_DST_RING_MAX]; struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX]; struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; @@ -346,9 +398,11 @@ struct ath12k_dp { struct ath12k_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX]; struct ath12k_spt_info *spt_info; u32 num_spt_pages; + u32 rx_ppt_base; + struct ath12k_rx_desc_info *rxbaddr[ATH12K_NUM_RX_SPT_PAGES]; + struct ath12k_tx_desc_info *txbaddr[ATH12K_NUM_TX_SPT_PAGES]; struct list_head rx_desc_free_list; - struct list_head rx_desc_used_list; - /* protects the free and used desc list */ + /* protects the free desc list */ spinlock_t rx_desc_lock; struct list_head tx_desc_free_list[ATH12K_HW_MAX_QUEUES]; @@ -361,23 +415,30 @@ struct ath12k_dp { struct dp_srng rxdma_err_dst_ring[MAX_RXDMA_PER_PDEV]; struct dp_rxdma_mon_ring rxdma_mon_buf_ring; struct dp_rxdma_mon_ring tx_mon_buf_ring; + struct dp_rxdma_mon_ring rx_mon_status_refill_ring[MAX_RXDMA_PER_PDEV]; struct ath12k_reo_q_addr_lut reoq_lut; + struct ath12k_reo_q_addr_lut ml_reoq_lut; }; /* HTT definitions */ +#define HTT_TAG_TCL_METADATA_VERSION 5 -#define HTT_TCL_META_DATA_TYPE BIT(0) -#define HTT_TCL_META_DATA_VALID_HTT BIT(1) +#define HTT_TCL_META_DATA_TYPE GENMASK(1, 0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(2) /* vdev meta data */ -#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) -#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) -#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(10, 3) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(12, 11) +#define HTT_TCL_META_DATA_HOST_INSPECTED_MISSION BIT(13) /* peer meta data */ -#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 3) -#define HTT_TX_WBM_COMP_STATUS_OFFSET 8 +/* Global sequence number */ +#define HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM 3 +#define HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED BIT(2) +#define HTT_TCL_META_DATA_GLOBAL_SEQ_NUM GENMASK(14, 3) +#define HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID 128 /* HTT tx completion is overlaid in wbm_release_ring */ #define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(16, 13) @@ -408,9 +469,15 @@ enum htt_h2t_msg_type { }; #define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) +#define HTT_OPTION_TCL_METADATA_VER_V2 2 +#define HTT_OPTION_TAG GENMASK(7, 0) +#define HTT_OPTION_LEN GENMASK(15, 8) +#define HTT_OPTION_VALUE GENMASK(31, 16) +#define HTT_TCL_METADATA_VER_SZ 4 struct htt_ver_req_cmd { __le32 ver_reg_info; + __le32 tcl_metadata_version; } __packed; enum htt_srng_ring_type { @@ -428,8 +495,11 @@ enum htt_srng_ring_id { HTT_HOST1_TO_FW_RXBUF_RING, HTT_HOST2_TO_FW_RXBUF_RING, HTT_RXDMA_NON_MONITOR_DEST_RING, + HTT_RXDMA_HOST_BUF_RING2, HTT_TX_MON_HOST2MON_BUF_RING, HTT_TX_MON_MON2HOST_DEST_RING, + HTT_RX_MON_HOST2MON_BUF_RING, + HTT_RX_MON_MON2HOST_DEST_RING, }; /* host -> target HTT_SRING_SETUP message @@ -690,9 +760,9 @@ enum htt_stats_internal_ppdu_frametype { * * The message would appear as follows: * - * |31 26|25|24|23 16|15 8|7 0| - * |-----------------+----------------+----------------+---------------| - * | rsvd1 |PS|SS| ring_id | pdev_id | msg_type | + * |31 29|28|27|26|25|24|23 16|15 8|7 0| + * |-------+--+--+--+--+--+-----------+----------------+---------------| + * | rsvd1 |ED|DT|OV|PS|SS| ring_id | pdev_id | msg_type | * |-------------------------------------------------------------------| * | rsvd2 | ring_buffer_size | * |-------------------------------------------------------------------| @@ -719,7 +789,13 @@ enum htt_stats_internal_ppdu_frametype { * More details can be got from enum htt_srng_ring_id * b'24 - status_swap: 1 is to swap status TLV * b'25 - pkt_swap: 1 is to swap packet TLV - * b'26:31 - rsvd1: reserved for future use + * b'26 - rx_offset_valid (OV): flag to indicate rx offsets + * configuration fields are valid + * b'27 - drop_thresh_valid (DT): flag to indicate if the + * rx_drop_threshold field is valid + * b'28 - rx_mon_global_en: Enable/Disable global register + * configuration in Rx monitor module. + * b'29:31 - rsvd1: reserved for future use * dword1 - b'0:16 - ring_buffer_size: size of buffers referenced by rx ring, * in byte units. * Valid only for HW_TO_SW_RING and SW_TO_HW_RING @@ -755,8 +831,22 @@ enum htt_stats_internal_ppdu_frametype { #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) -#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) -#define HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID BIT(26) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID BIT(26) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL BIT(27) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON BIT(28) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT GENMASK(18, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL GENMASK(21, 19) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA GENMASK(24, 22) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD GENMASK(9, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE BIT(17) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE BIT(18) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE BIT(19) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET BIT(0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET GENMASK(14, 1) #define HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET GENMASK(15, 0) #define HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET GENMASK(31, 16) @@ -785,6 +875,7 @@ enum htt_rx_filter_tlv_flags { HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO = BIT(13), }; enum htt_rx_mgmt_pkt_filter_tlv_flags0 { @@ -1073,6 +1164,21 @@ enum htt_rx_data_pkt_filter_tlv_flasg3 { HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ HTT_RX_FILTER_TLV_FLAGS_ATTENTION) +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO) + /* msdu start. mpdu end, attention, rx hdr tlv's are not subscribed */ #define HTT_RX_TLV_FLAGS_RXDMA_RING \ (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ @@ -1101,6 +1207,10 @@ struct htt_rx_ring_selection_cfg_cmd { __le32 info3; } __packed; +#define HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE 32 +#define HTT_RX_RING_DEFAULT_DMA_LENGTH 0x7 +#define HTT_RX_RING_PKT_TLV_OFFSET 0x1 + struct htt_rx_ring_tlv_filter { u32 rx_filter; /* see htt_rx_filter_tlv_flags */ u32 pkt_filter_flags0; /* MGMT */ @@ -1118,6 +1228,17 @@ struct htt_rx_ring_tlv_filter { u16 rx_mpdu_start_wmask; u16 rx_mpdu_end_wmask; u32 rx_msdu_end_wmask; + u32 conf_len_ctrl; + u32 conf_len_mgmt; + u32 conf_len_data; + u16 rx_drop_threshold; + bool enable_log_mgmt_type; + bool enable_log_ctrl_type; + bool enable_log_data_type; + bool enable_rx_tlv_offset; + u16 rx_tlv_offset; + bool drop_threshold_valid; + bool rxmon_disable; }; #define HTT_STATS_FRAME_CTRL_TYPE_MGMT 0x0 @@ -1235,6 +1356,8 @@ struct htt_t2h_version_conf_msg { #define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) #define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16) #define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0) +#define HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID GENMASK(15, 0) +#define HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL GENMASK(31, 16) #define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16) #define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16 @@ -1492,18 +1615,6 @@ struct htt_ppdu_stats_user_rate { #define HTT_TX_INFO_PEERID(_flags) \ u32_get_bits(_flags, HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M) -struct htt_tx_ppdu_stats_info { - struct htt_tlv tlv_hdr; - __le32 tx_success_bytes; - __le32 tx_retry_bytes; - __le32 tx_failed_bytes; - __le32 flags; /* %HTT_PPDU_STATS_TX_INFO_FLAGS_ */ - __le16 tx_success_msdus; - __le16 tx_retry_msdus; - __le16 tx_failed_msdus; - __le16 tx_duration; /* united in us */ -} __packed; - enum htt_ppdu_stats_usr_compln_status { HTT_PPDU_STATS_USER_STATUS_OK, HTT_PPDU_STATS_USER_STATUS_FILTERED, @@ -1798,6 +1909,18 @@ enum vdev_stats_offload_timer_duration { ATH12K_STATS_TIMER_DUR_2SEC = 3, }; +#define ATH12K_HTT_MAC_ADDR_L32_0 GENMASK(7, 0) +#define ATH12K_HTT_MAC_ADDR_L32_1 GENMASK(15, 8) +#define ATH12K_HTT_MAC_ADDR_L32_2 GENMASK(23, 16) +#define ATH12K_HTT_MAC_ADDR_L32_3 GENMASK(31, 24) +#define ATH12K_HTT_MAC_ADDR_H16_0 GENMASK(7, 0) +#define ATH12K_HTT_MAC_ADDR_H16_1 GENMASK(15, 8) + +struct htt_mac_addr { + __le32 mac_addr_l32; + __le32 mac_addr_h16; +} __packed; + static inline void ath12k_dp_get_mac_addr(u32 addr_l32, u16 addr_h16, u8 *addr) { memcpy(addr, &addr_l32, 4); @@ -1808,12 +1931,13 @@ int ath12k_dp_service_srng(struct ath12k_base *ab, struct ath12k_ext_irq_grp *irq_grp, int budget); int ath12k_dp_htt_connect(struct ath12k_dp *dp); -void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_vif *arvif); +void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_free(struct ath12k_base *ab); int ath12k_dp_alloc(struct ath12k_base *ab); void ath12k_dp_cc_config(struct ath12k_base *ab); +void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); -void ath12k_dp_pdev_pre_alloc(struct ath12k_base *ab); +void ath12k_dp_pdev_pre_alloc(struct ath12k *ar); void ath12k_dp_pdev_free(struct ath12k_base *ab); int ath12k_dp_tx_htt_srng_setup(struct ath12k_base *ab, u32 ring_id, int mac_id, enum hal_ring_type ring_type); diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 2d56913a75d0..28cadc4167f7 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "dp_mon.h" @@ -10,11 +10,16 @@ #include "dp_tx.h" #include "peer.h" -static void ath12k_dp_mon_rx_handle_ofdma_info(void *rx_tlv, - struct hal_rx_user_status *rx_user_status) -{ - struct hal_rx_ppdu_end_user_stats *ppdu_end_user = rx_tlv; +#define ATH12K_LE32_DEC_ENC(value, dec_bits, enc_bits) \ + u32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + +#define ATH12K_LE64_DEC_ENC(value, dec_bits, enc_bits) \ + u32_encode_bits(le64_get_bits(value, dec_bits), enc_bits) +static void +ath12k_dp_mon_rx_handle_ofdma_info(const struct hal_rx_ppdu_end_user_stats *ppdu_end_user, + struct hal_rx_user_status *rx_user_status) +{ rx_user_status->ul_ofdma_user_v0_word0 = __le32_to_cpu(ppdu_end_user->usr_resp_ref); rx_user_status->ul_ofdma_user_v0_word1 = @@ -26,19 +31,16 @@ ath12k_dp_mon_rx_populate_byte_count(const struct hal_rx_ppdu_end_user_stats *st void *ppduinfo, struct hal_rx_user_status *rx_user_status) { - u32 mpdu_ok_byte_count = __le32_to_cpu(stats->mpdu_ok_cnt); - u32 mpdu_err_byte_count = __le32_to_cpu(stats->mpdu_err_cnt); - rx_user_status->mpdu_ok_byte_count = - u32_get_bits(mpdu_ok_byte_count, - HAL_RX_PPDU_END_USER_STATS_MPDU_DELIM_OK_BYTE_COUNT); + le32_get_bits(stats->info7, + HAL_RX_PPDU_END_USER_STATS_INFO7_MPDU_OK_BYTE_COUNT); rx_user_status->mpdu_err_byte_count = - u32_get_bits(mpdu_err_byte_count, - HAL_RX_PPDU_END_USER_STATS_MPDU_DELIM_ERR_BYTE_COUNT); + le32_get_bits(stats->info8, + HAL_RX_PPDU_END_USER_STATS_INFO8_MPDU_ERR_BYTE_COUNT); } static void -ath12k_dp_mon_rx_populate_mu_user_info(void *rx_tlv, +ath12k_dp_mon_rx_populate_mu_user_info(const struct hal_rx_ppdu_end_user_stats *rx_tlv, struct hal_rx_mon_ppdu_info *ppdu_info, struct hal_rx_user_status *rx_user_status) { @@ -76,12 +78,10 @@ ath12k_dp_mon_rx_populate_mu_user_info(void *rx_tlv, ath12k_dp_mon_rx_populate_byte_count(rx_tlv, ppdu_info, rx_user_status); } -static void ath12k_dp_mon_parse_vht_sig_a(u8 *tlv_data, +static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vht_sig, struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_vht_sig_a_info *vht_sig = - (struct hal_rx_vht_sig_a_info *)tlv_data; - u32 nsts, group_id, info0, info1; + u32 nsts, info0, info1; u8 gi_setting; info0 = __le32_to_cpu(vht_sig->info0); @@ -109,12 +109,8 @@ static void ath12k_dp_mon_parse_vht_sig_a(u8 *tlv_data, ppdu_info->bw = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_BW); ppdu_info->beamformed = u32_get_bits(info1, HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED); - group_id = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID); - if (group_id == 0 || group_id == 63) - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; - else - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; - ppdu_info->vht_flag_values5 = group_id; + ppdu_info->vht_flag_values5 = u32_get_bits(info0, + HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID); ppdu_info->vht_flag_values3[0] = (((ppdu_info->mcs) << 4) | ppdu_info->nss); ppdu_info->vht_flag_values2 = ppdu_info->bw; @@ -122,11 +118,9 @@ static void ath12k_dp_mon_parse_vht_sig_a(u8 *tlv_data, u32_get_bits(info1, HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING); } -static void ath12k_dp_mon_parse_ht_sig(u8 *tlv_data, +static void ath12k_dp_mon_parse_ht_sig(const struct hal_rx_ht_sig_info *ht_sig, struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_ht_sig_info *ht_sig = - (struct hal_rx_ht_sig_info *)tlv_data; u32 info0 = __le32_to_cpu(ht_sig->info0); u32 info1 = __le32_to_cpu(ht_sig->info1); @@ -136,14 +130,11 @@ static void ath12k_dp_mon_parse_ht_sig(u8 *tlv_data, ppdu_info->ldpc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING); ppdu_info->gi = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_GI); ppdu_info->nss = (ppdu_info->mcs >> 3); - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } -static void ath12k_dp_mon_parse_l_sig_b(u8 *tlv_data, +static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_lsig_b_info *lsigb = - (struct hal_rx_lsig_b_info *)tlv_data; u32 info0 = __le32_to_cpu(lsigb->info0); u8 rate; @@ -170,14 +161,11 @@ static void ath12k_dp_mon_parse_l_sig_b(u8 *tlv_data, ppdu_info->rate = rate; ppdu_info->cck_flag = 1; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } -static void ath12k_dp_mon_parse_l_sig_a(u8 *tlv_data, +static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_lsig_a_info *lsiga = - (struct hal_rx_lsig_a_info *)tlv_data; u32 info0 = __le32_to_cpu(lsiga->info0); u8 rate; @@ -212,17 +200,15 @@ static void ath12k_dp_mon_parse_l_sig_a(u8 *tlv_data, } ppdu_info->rate = rate; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } -static void ath12k_dp_mon_parse_he_sig_b2_ofdma(u8 *tlv_data, - struct hal_rx_mon_ppdu_info *ppdu_info) +static void +ath12k_dp_mon_parse_he_sig_b2_ofdma(const struct hal_rx_he_sig_b2_ofdma_info *ofdma, + struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_he_sig_b2_ofdma_info *he_sig_b2_ofdma = - (struct hal_rx_he_sig_b2_ofdma_info *)tlv_data; u32 info0, value; - info0 = __le32_to_cpu(he_sig_b2_ofdma->info0); + info0 = __le32_to_cpu(ofdma->info0); ppdu_info->he_data1 |= HE_MCS_KNOWN | HE_DCM_KNOWN | HE_CODING_KNOWN; @@ -250,14 +236,12 @@ static void ath12k_dp_mon_parse_he_sig_b2_ofdma(u8 *tlv_data, ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS); ppdu_info->beamformed = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF); - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; } -static void ath12k_dp_mon_parse_he_sig_b2_mu(u8 *tlv_data, - struct hal_rx_mon_ppdu_info *ppdu_info) +static void +ath12k_dp_mon_parse_he_sig_b2_mu(const struct hal_rx_he_sig_b2_mu_info *he_sig_b2_mu, + struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_he_sig_b2_mu_info *he_sig_b2_mu = - (struct hal_rx_he_sig_b2_mu_info *)tlv_data; u32 info0, value; info0 = __le32_to_cpu(he_sig_b2_mu->info0); @@ -280,11 +264,10 @@ static void ath12k_dp_mon_parse_he_sig_b2_mu(u8 *tlv_data, ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS); } -static void ath12k_dp_mon_parse_he_sig_b1_mu(u8 *tlv_data, - struct hal_rx_mon_ppdu_info *ppdu_info) +static void +ath12k_dp_mon_parse_he_sig_b1_mu(const struct hal_rx_he_sig_b1_mu_info *he_sig_b1_mu, + struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_he_sig_b1_mu_info *he_sig_b1_mu = - (struct hal_rx_he_sig_b1_mu_info *)tlv_data; u32 info0 = __le32_to_cpu(he_sig_b1_mu->info0); u16 ru_tones; @@ -292,14 +275,12 @@ static void ath12k_dp_mon_parse_he_sig_b1_mu(u8 *tlv_data, HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION); ppdu_info->ru_alloc = ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); ppdu_info->he_RU[0] = ru_tones; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; } -static void ath12k_dp_mon_parse_he_sig_mu(u8 *tlv_data, - struct hal_rx_mon_ppdu_info *ppdu_info) +static void +ath12k_dp_mon_parse_he_sig_mu(const struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_mu_dl, + struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_mu_dl = - (struct hal_rx_he_sig_a_mu_dl_info *)tlv_data; u32 info0, info1, value; u16 he_gi = 0, he_ltf = 0; @@ -427,14 +408,11 @@ static void ath12k_dp_mon_parse_he_sig_mu(u8 *tlv_data, ppdu_info->is_stbc = info1 & HAL_RX_HE_SIG_A_MU_DL_INFO1_STBC; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; } -static void ath12k_dp_mon_parse_he_sig_su(u8 *tlv_data, +static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *he_sig_a, struct hal_rx_mon_ppdu_info *ppdu_info) { - struct hal_rx_he_sig_a_su_info *he_sig_a = - (struct hal_rx_he_sig_a_su_info *)tlv_data; u32 info0, info1, value; u32 dcm; u8 he_dcm = 0, he_stbc = 0; @@ -577,28 +555,940 @@ static void ath12k_dp_mon_parse_he_sig_su(u8 *tlv_data, dcm = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM); ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS); ppdu_info->dcm = dcm; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_cmn(const struct hal_mon_usig_cmn *cmn, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + u32 common; + + ppdu_info->u_sig_info.bw = le32_get_bits(cmn->info0, + HAL_RX_USIG_CMN_INFO0_BW); + ppdu_info->u_sig_info.ul_dl = le32_get_bits(cmn->info0, + HAL_RX_USIG_CMN_INFO0_UL_DL); + + common = __le32_to_cpu(ppdu_info->u_sig_info.usig.common); + common |= IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_PHY_VERSION, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER) | + u32_encode_bits(ppdu_info->u_sig_info.bw, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) | + u32_encode_bits(ppdu_info->u_sig_info.ul_dl, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_BSS_COLOR, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_TXOP, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + ppdu_info->u_sig_info.usig.common = cpu_to_le32(common); + + switch (ppdu_info->u_sig_info.bw) { + default: + fallthrough; + case HAL_EHT_BW_20: + ppdu_info->bw = HAL_RX_BW_20MHZ; + break; + case HAL_EHT_BW_40: + ppdu_info->bw = HAL_RX_BW_40MHZ; + break; + case HAL_EHT_BW_80: + ppdu_info->bw = HAL_RX_BW_80MHZ; + break; + case HAL_EHT_BW_160: + ppdu_info->bw = HAL_RX_BW_160MHZ; + break; + case HAL_EHT_BW_320_1: + case HAL_EHT_BW_320_2: + ppdu_info->bw = HAL_RX_BW_320MHZ; + break; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_tb(const struct hal_mon_usig_tb *usig_tb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig; + enum ieee80211_radiotap_eht_usig_tb spatial_reuse1, spatial_reuse2; + u32 common, value, mask; + + spatial_reuse1 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1; + spatial_reuse2 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2; + + common = __le32_to_cpu(usig->common); + value = __le32_to_cpu(usig->value); + mask = __le32_to_cpu(usig->mask); + + ppdu_info->u_sig_info.ppdu_type_comp_mode = + le32_get_bits(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE); + + common |= ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + value |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | + u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE) | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1, + spatial_reuse1) | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2, + spatial_reuse2) | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC) | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_TAIL, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL); + + mask |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | + spatial_reuse1 | spatial_reuse2 | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL; + + usig->common = cpu_to_le32(common); + usig->value = cpu_to_le32(value); + usig->mask = cpu_to_le32(mask); +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig; + enum ieee80211_radiotap_eht_usig_mu sig_symb, punc; + u32 common, value, mask; + + sig_symb = IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS; + punc = IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO; + + common = __le32_to_cpu(usig->common); + value = __le32_to_cpu(usig->value); + mask = __le32_to_cpu(usig->mask); + + ppdu_info->u_sig_info.ppdu_type_comp_mode = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); + ppdu_info->u_sig_info.eht_sig_mcs = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS); + ppdu_info->u_sig_info.num_eht_sig_sym = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM); + + common |= ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + value |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | + u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE) | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO, + punc) | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | + u32_encode_bits(ppdu_info->u_sig_info.eht_sig_mcs, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS) | + u32_encode_bits(ppdu_info->u_sig_info.num_eht_sig_sym, + sig_symb) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_TAIL, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL); + + mask |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | + punc | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS | + sig_symb | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL; + + usig->common = cpu_to_le32(common); + usig->value = cpu_to_le32(value); + usig->mask = cpu_to_le32(mask); +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_hdr(const struct hal_mon_usig_hdr *usig, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + u8 comp_mode; + + ppdu_info->eht_usig = true; + + ath12k_dp_mon_hal_rx_parse_u_sig_cmn(&usig->cmn, ppdu_info); + + comp_mode = le32_get_bits(usig->non_cmn.mu.info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); + + if (comp_mode == 0 && ppdu_info->u_sig_info.ul_dl) + ath12k_dp_mon_hal_rx_parse_u_sig_tb(&usig->non_cmn.tb, ppdu_info); + else + ath12k_dp_mon_hal_rx_parse_u_sig_mu(&usig->non_cmn.mu, ppdu_info); +} + +static void +ath12k_dp_mon_hal_aggr_tlv(struct hal_rx_mon_ppdu_info *ppdu_info, + u16 tlv_len, const void *tlv_data) +{ + if (tlv_len <= HAL_RX_MON_MAX_AGGR_SIZE - ppdu_info->tlv_aggr.cur_len) { + memcpy(ppdu_info->tlv_aggr.buf + ppdu_info->tlv_aggr.cur_len, + tlv_data, tlv_len); + ppdu_info->tlv_aggr.cur_len += tlv_len; + } +} + +static inline bool +ath12k_dp_mon_hal_rx_is_frame_type_ndp(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == 1 && + usig_info->eht_sig_mcs == 0 && + usig_info->num_eht_sig_sym == 0) + return true; + + return false; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_non_ofdma(const struct hal_rx_u_sig_info *usig_info) +{ + u32 ppdu_type_comp_mode = usig_info->ppdu_type_comp_mode; + u32 ul_dl = usig_info->ul_dl; + + if ((ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 0) || + (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_OFDMA && ul_dl == 0) || + (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 1)) + return true; + + return false; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_ofdma(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == 0 && usig_info->ul_dl == 0) + return true; + + return false; +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(const struct hal_eht_sig_ndp_cmn_eb *eht_sig_ndp, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE | + IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF | + IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | + IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S | + IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_S | + IEEE80211_RADIOTAP_EHT_KNOWN_CRC1 | + IEEE80211_RADIOTAP_EHT_KNOWN_TAIL1; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[0]); + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + /* GI and LTF size are separately indicated in radiotap header + * and hence will be parsed from other TLV + */ + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_DATA0_CRC1_O); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD, + IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_S); + eht->data[0] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[7]); + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS, + IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED, + IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); + eht->data[7] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_usig_overflow(const struct hal_eht_sig_usig_overflow *ovflow, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE | + IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF | + IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_O; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[0]); + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + + /* GI and LTF size are separately indicated in radiotap header + * and hence will be parsed from other TLV + */ + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR, + IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY, + IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD, + IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_O); + eht->data[0] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_non_ofdma_users(const struct hal_eht_sig_non_ofdma_cmn_eb *eb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[7]); + data |= ATH12K_LE32_DEC_ENC(eb->info0, + HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS, + IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + eht->data[7] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(const struct hal_eht_sig_mu_mimo *user, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info; + u32 user_idx; + + if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info)) + return; + + user_idx = eht_info->num_user_info++; + + eht_info->user_info[user_idx] |= + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_KNOWN_M | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS, + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_M); + + ppdu_info->mcs = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS); +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(const struct hal_eht_sig_non_mu_mimo *user, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info; + u32 user_idx; + + if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info)) + return; + + user_idx = eht_info->num_user_info++; + + eht_info->user_info[user_idx] |= + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS, + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS, + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED, + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + ppdu_info->mcs = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS); + + ppdu_info->nss = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS) + 1; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_mu_mimo_user(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_SU && + usig_info->ul_dl == 1) + return true; + + return false; +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(const void *tlv, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_non_ofdma_cmn_eb *eb = tlv; + + ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info); + ath12k_dp_mon_hal_rx_parse_non_ofdma_users(eb, ppdu_info); + + if (ath12k_dp_mon_hal_rx_is_mu_mimo_user(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(&eb->user_field.mu_mimo, + ppdu_info); + else + ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&eb->user_field.n_mu_mimo, + ppdu_info); +} + +static void +ath12k_dp_mon_hal_rx_parse_ru_allocation(const struct hal_eht_sig_ofdma_cmn_eb *eb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_ofdma_cmn_eb1 *ofdma_cmn_eb1 = &eb->eb1; + const struct hal_eht_sig_ofdma_cmn_eb2 *ofdma_cmn_eb2 = &eb->eb2; + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + enum ieee80211_radiotap_eht_data ru_123, ru_124, ru_125, ru_126; + enum ieee80211_radiotap_eht_data ru_121, ru_122, ru_112, ru_111; + u32 data; + + ru_123 = IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3; + ru_124 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4; + ru_125 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5; + ru_126 = IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6; + ru_121 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1; + ru_122 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2; + ru_112 = IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2; + ru_111 = IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1; + + switch (ppdu_info->u_sig_info.bw) { + case HAL_EHT_BW_320_2: + case HAL_EHT_BW_320_1: + data = __le32_to_cpu(eht->data[4]); + /* CC1 2::3 */ + data |= IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3, + ru_123); + eht->data[4] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[5]); + /* CC1 2::4 */ + data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4, + ru_124); + + /* CC1 2::5 */ + data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5, + ru_125); + eht->data[5] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[6]); + /* CC1 2::6 */ + data |= IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6, + ru_126); + eht->data[6] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_160: + data = __le32_to_cpu(eht->data[3]); + /* CC1 2::1 */ + data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1, + ru_121); + /* CC1 2::2 */ + data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2, + ru_122); + eht->data[3] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_80: + data = __le32_to_cpu(eht->data[2]); + /* CC1 1::2 */ + data |= IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0, + HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2, + ru_112); + eht->data[2] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_40: + fallthrough; + case HAL_EHT_BW_20: + data = __le32_to_cpu(eht->data[1]); + /* CC1 1::1 */ + data |= IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0, + HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1, + ru_111); + eht->data[1] = cpu_to_le32(data); + break; + default: + break; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(const void *tlv, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_ofdma_cmn_eb *ofdma = tlv; + + ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info); + ath12k_dp_mon_hal_rx_parse_ru_allocation(ofdma, ppdu_info); + + ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&ofdma->user_field.n_mu_mimo, + ppdu_info); +} + +static void +ath12k_dp_mon_parse_eht_sig_hdr(struct hal_rx_mon_ppdu_info *ppdu_info, + const void *tlv_data) +{ + ppdu_info->is_eht = true; + + if (ath12k_dp_mon_hal_rx_is_frame_type_ndp(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(tlv_data, ppdu_info); + else if (ath12k_dp_mon_hal_rx_is_non_ofdma(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(tlv_data, ppdu_info); + else if (ath12k_dp_mon_hal_rx_is_ofdma(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(tlv_data, ppdu_info); +} + +static inline enum ath12k_eht_ru_size +hal_rx_mon_hal_ru_size_to_ath12k_ru_size(u32 hal_ru_size) +{ + switch (hal_ru_size) { + case HAL_EHT_RU_26: + return ATH12K_EHT_RU_26; + case HAL_EHT_RU_52: + return ATH12K_EHT_RU_52; + case HAL_EHT_RU_78: + return ATH12K_EHT_RU_52_26; + case HAL_EHT_RU_106: + return ATH12K_EHT_RU_106; + case HAL_EHT_RU_132: + return ATH12K_EHT_RU_106_26; + case HAL_EHT_RU_242: + return ATH12K_EHT_RU_242; + case HAL_EHT_RU_484: + return ATH12K_EHT_RU_484; + case HAL_EHT_RU_726: + return ATH12K_EHT_RU_484_242; + case HAL_EHT_RU_996: + return ATH12K_EHT_RU_996; + case HAL_EHT_RU_996x2: + return ATH12K_EHT_RU_996x2; + case HAL_EHT_RU_996x3: + return ATH12K_EHT_RU_996x3; + case HAL_EHT_RU_996x4: + return ATH12K_EHT_RU_996x4; + case HAL_EHT_RU_NONE: + return ATH12K_EHT_RU_INVALID; + case HAL_EHT_RU_996_484: + return ATH12K_EHT_RU_996_484; + case HAL_EHT_RU_996x2_484: + return ATH12K_EHT_RU_996x2_484; + case HAL_EHT_RU_996x3_484: + return ATH12K_EHT_RU_996x3_484; + case HAL_EHT_RU_996_484_242: + return ATH12K_EHT_RU_996_484_242; + default: + return ATH12K_EHT_RU_INVALID; + } +} + +static inline u32 +hal_rx_ul_ofdma_ru_size_to_width(enum ath12k_eht_ru_size ru_size) +{ + switch (ru_size) { + case ATH12K_EHT_RU_26: + return RU_26; + case ATH12K_EHT_RU_52: + return RU_52; + case ATH12K_EHT_RU_52_26: + return RU_52_26; + case ATH12K_EHT_RU_106: + return RU_106; + case ATH12K_EHT_RU_106_26: + return RU_106_26; + case ATH12K_EHT_RU_242: + return RU_242; + case ATH12K_EHT_RU_484: + return RU_484; + case ATH12K_EHT_RU_484_242: + return RU_484_242; + case ATH12K_EHT_RU_996: + return RU_996; + case ATH12K_EHT_RU_996_484: + return RU_996_484; + case ATH12K_EHT_RU_996_484_242: + return RU_996_484_242; + case ATH12K_EHT_RU_996x2: + return RU_2X996; + case ATH12K_EHT_RU_996x2_484: + return RU_2X996_484; + case ATH12K_EHT_RU_996x3: + return RU_3X996; + case ATH12K_EHT_RU_996x3_484: + return RU_3X996_484; + case ATH12K_EHT_RU_996x4: + return RU_4X996; + default: + return RU_INVALID; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *rx_usr_info, + u16 user_id, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_user_status *mon_rx_user_status = NULL; + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + enum ath12k_eht_ru_size rtap_ru_size = ATH12K_EHT_RU_INVALID; + u32 ru_width, reception_type, ru_index = HAL_EHT_RU_INVALID; + u32 ru_type_80_0, ru_start_index_80_0; + u32 ru_type_80_1, ru_start_index_80_1; + u32 ru_type_80_2, ru_start_index_80_2; + u32 ru_type_80_3, ru_start_index_80_3; + u32 ru_size = 0, num_80mhz_with_ru = 0; + u64 ru_index_320mhz = 0; + u32 ru_index_per80mhz; + + reception_type = le32_get_bits(rx_usr_info->info0, + HAL_RX_USR_INFO0_RECEPTION_TYPE); + + switch (reception_type) { + case HAL_RECEPTION_TYPE_SU: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + case HAL_RECEPTION_TYPE_DL_MU_MIMO: + case HAL_RECEPTION_TYPE_UL_MU_MIMO: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + case HAL_RECEPTION_TYPE_DL_MU_OFMA: + case HAL_RECEPTION_TYPE_UL_MU_OFDMA: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; + break; + case HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO: + case HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO; + } + + ppdu_info->is_stbc = le32_get_bits(rx_usr_info->info0, HAL_RX_USR_INFO0_STBC); + ppdu_info->ldpc = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_LDPC); + ppdu_info->dcm = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_STA_DCM); + ppdu_info->bw = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_RX_BW); + ppdu_info->mcs = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_MCS); + ppdu_info->nss = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_NSS) + 1; + + if (user_id < HAL_MAX_UL_MU_USERS) { + mon_rx_user_status = &ppdu_info->userstats[user_id]; + mon_rx_user_status->mcs = ppdu_info->mcs; + mon_rx_user_status->nss = ppdu_info->nss; + } + + if (!(ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_MIMO || + ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA || + ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO)) + return; + + /* RU allocation present only for OFDMA reception */ + ru_type_80_0 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_0); + ru_start_index_80_0 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_0); + if (ru_type_80_0 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_0; + ru_index_per80mhz = ru_start_index_80_0; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_0, 0, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_1 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_1); + ru_start_index_80_1 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_1); + if (ru_type_80_1 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_1; + ru_index_per80mhz = ru_start_index_80_1; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_1, 1, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_2 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_2); + ru_start_index_80_2 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_2); + if (ru_type_80_2 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_2; + ru_index_per80mhz = ru_start_index_80_2; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_2, 2, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_3 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_3); + ru_start_index_80_3 = le32_get_bits(rx_usr_info->info2, + HAL_RX_USR_INFO3_RU_START_IDX_80_3); + if (ru_type_80_3 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_3; + ru_index_per80mhz = ru_start_index_80_3; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_3, 3, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + if (num_80mhz_with_ru > 1) { + /* Calculate the MRU index */ + switch (ru_index_320mhz) { + case HAL_EHT_RU_996_484_0: + case HAL_EHT_RU_996x2_484_0: + case HAL_EHT_RU_996x3_484_0: + ru_index = 0; + break; + case HAL_EHT_RU_996_484_1: + case HAL_EHT_RU_996x2_484_1: + case HAL_EHT_RU_996x3_484_1: + ru_index = 1; + break; + case HAL_EHT_RU_996_484_2: + case HAL_EHT_RU_996x2_484_2: + case HAL_EHT_RU_996x3_484_2: + ru_index = 2; + break; + case HAL_EHT_RU_996_484_3: + case HAL_EHT_RU_996x2_484_3: + case HAL_EHT_RU_996x3_484_3: + ru_index = 3; + break; + case HAL_EHT_RU_996_484_4: + case HAL_EHT_RU_996x2_484_4: + case HAL_EHT_RU_996x3_484_4: + ru_index = 4; + break; + case HAL_EHT_RU_996_484_5: + case HAL_EHT_RU_996x2_484_5: + case HAL_EHT_RU_996x3_484_5: + ru_index = 5; + break; + case HAL_EHT_RU_996_484_6: + case HAL_EHT_RU_996x2_484_6: + case HAL_EHT_RU_996x3_484_6: + ru_index = 6; + break; + case HAL_EHT_RU_996_484_7: + case HAL_EHT_RU_996x2_484_7: + case HAL_EHT_RU_996x3_484_7: + ru_index = 7; + break; + case HAL_EHT_RU_996x2_484_8: + ru_index = 8; + break; + case HAL_EHT_RU_996x2_484_9: + ru_index = 9; + break; + case HAL_EHT_RU_996x2_484_10: + ru_index = 10; + break; + case HAL_EHT_RU_996x2_484_11: + ru_index = 11; + break; + default: + ru_index = HAL_EHT_RU_INVALID; + break; + } + + ru_size += 4; + } + + rtap_ru_size = hal_rx_mon_hal_ru_size_to_ath12k_ru_size(ru_size); + if (rtap_ru_size != ATH12K_EHT_RU_INVALID) { + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_SIZE_OM; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[1]); + data |= u32_encode_bits(rtap_ru_size, + IEEE80211_RADIOTAP_EHT_DATA1_RU_SIZE); + eht->data[1] = cpu_to_le32(data); + } + + if (ru_index != HAL_EHT_RU_INVALID) { + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_INDEX_OM; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[1]); + data |= u32_encode_bits(rtap_ru_size, + IEEE80211_RADIOTAP_EHT_DATA1_RU_INDEX); + eht->data[1] = cpu_to_le32(data); + } + + if (mon_rx_user_status && ru_index != HAL_EHT_RU_INVALID && + rtap_ru_size != ATH12K_EHT_RU_INVALID) { + mon_rx_user_status->ul_ofdma_ru_start_index = ru_index; + mon_rx_user_status->ul_ofdma_ru_size = rtap_ru_size; + + ru_width = hal_rx_ul_ofdma_ru_size_to_width(rtap_ru_size); + + mon_rx_user_status->ul_ofdma_ru_width = ru_width; + mon_rx_user_status->ofdma_info_valid = 1; + } +} + +static void ath12k_dp_mon_parse_rx_msdu_end_err(u32 info, u32 *errmap) +{ + 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; +} + +static void +ath12k_dp_mon_parse_status_msdu_end(struct ath12k_mon_data *pmon, + const struct hal_rx_msdu_end *msdu_end) +{ + ath12k_dp_mon_parse_rx_msdu_end_err(__le32_to_cpu(msdu_end->info2), + &pmon->err_bitmap); + pmon->decap_format = le32_get_bits(msdu_end->info1, + RX_MSDU_END_INFO11_DECAP_FORMAT); } static enum hal_rx_mon_status -ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, +ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, - u32 tlv_tag, u8 *tlv_data, u32 userid) + const struct hal_tlv_64_hdr *tlv) { struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; - u32 info[7]; + const void *tlv_data = tlv->value; + u32 info[7], userid; + u16 tlv_tag, tlv_len; + + tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); + tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); + userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID); + + if (ppdu_info->tlv_aggr.in_progress && ppdu_info->tlv_aggr.tlv_tag != tlv_tag) { + ath12k_dp_mon_parse_eht_sig_hdr(ppdu_info, ppdu_info->tlv_aggr.buf); + + ppdu_info->tlv_aggr.in_progress = false; + ppdu_info->tlv_aggr.cur_len = 0; + } switch (tlv_tag) { case HAL_RX_PPDU_START: { - struct hal_rx_ppdu_start *ppdu_start = - (struct hal_rx_ppdu_start *)tlv_data; + const struct hal_rx_ppdu_start *ppdu_start = tlv_data; + + u64 ppdu_ts = ath12k_le32hilo_to_u64(ppdu_start->ppdu_start_ts_63_32, + ppdu_start->ppdu_start_ts_31_0); info[0] = __le32_to_cpu(ppdu_start->info0); - ppdu_info->ppdu_id = - u32_get_bits(info[0], HAL_RX_PPDU_START_INFO0_PPDU_ID); - ppdu_info->chan_num = __le32_to_cpu(ppdu_start->chan_num); - ppdu_info->ppdu_ts = __le32_to_cpu(ppdu_start->ppdu_start_ts); + ppdu_info->ppdu_id = u32_get_bits(info[0], + HAL_RX_PPDU_START_INFO0_PPDU_ID); + + info[1] = __le32_to_cpu(ppdu_start->info1); + ppdu_info->chan_num = u32_get_bits(info[1], + HAL_RX_PPDU_START_INFO1_CHAN_NUM); + ppdu_info->freq = u32_get_bits(info[1], + HAL_RX_PPDU_START_INFO1_CHAN_FREQ); + ppdu_info->ppdu_ts = ppdu_ts; if (ppdu_info->ppdu_id != ppdu_info->last_ppdu_id) { ppdu_info->last_ppdu_id = ppdu_info->ppdu_id; @@ -610,8 +1500,8 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, break; } case HAL_RX_PPDU_END_USER_STATS: { - struct hal_rx_ppdu_end_user_stats *eu_stats = - (struct hal_rx_ppdu_end_user_stats *)tlv_data; + const struct hal_rx_ppdu_end_user_stats *eu_stats = tlv_data; + u32 tid_bitmap; info[0] = __le32_to_cpu(eu_stats->info0); info[1] = __le32_to_cpu(eu_stats->info1); @@ -624,10 +1514,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, u32_get_bits(info[2], HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX); ppdu_info->fc_valid = u32_get_bits(info[1], HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID); - ppdu_info->tid = - ffs(u32_get_bits(info[6], - HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP) - - 1); + tid_bitmap = u32_get_bits(info[6], + HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP); + ppdu_info->tid = ffs(tid_bitmap) - 1; ppdu_info->tcp_msdu_count = u32_get_bits(info[4], HAL_RX_PPDU_END_USER_STATS_INFO4_TCP_MSDU_CNT); @@ -649,6 +1538,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, ppdu_info->num_mpdu_fcs_err = u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR); + ppdu_info->peer_id = + u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_PEER_ID); + switch (ppdu_info->preamble_type) { case HAL_RX_PREAMBLE_11N: ppdu_info->ht_flags = 1; @@ -659,6 +1551,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, case HAL_RX_PREAMBLE_11AX: ppdu_info->he_flags = 1; break; + case HAL_RX_PREAMBLE_11BE: + ppdu_info->is_eht = true; + break; default: break; } @@ -666,10 +1561,15 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, if (userid < HAL_MAX_UL_MU_USERS) { struct hal_rx_user_status *rxuser_stats = &ppdu_info->userstats[userid]; + + if (ppdu_info->num_mpdu_fcs_ok > 1 || + ppdu_info->num_mpdu_fcs_err > 1) + ppdu_info->userstats[userid].ampdu_present = true; + ppdu_info->num_users += 1; - ath12k_dp_mon_rx_handle_ofdma_info(tlv_data, rxuser_stats); - ath12k_dp_mon_rx_populate_mu_user_info(tlv_data, ppdu_info, + ath12k_dp_mon_rx_handle_ofdma_info(eu_stats, rxuser_stats); + ath12k_dp_mon_rx_populate_mu_user_info(eu_stats, ppdu_info, rxuser_stats); } ppdu_info->mpdu_fcs_ok_bitmap[0] = __le32_to_cpu(eu_stats->rsvd1[0]); @@ -677,8 +1577,8 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, break; } case HAL_RX_PPDU_END_USER_STATS_EXT: { - struct hal_rx_ppdu_end_user_stats_ext *eu_stats = - (struct hal_rx_ppdu_end_user_stats_ext *)tlv_data; + const struct hal_rx_ppdu_end_user_stats_ext *eu_stats = tlv_data; + ppdu_info->mpdu_fcs_ok_bitmap[2] = __le32_to_cpu(eu_stats->info1); ppdu_info->mpdu_fcs_ok_bitmap[3] = __le32_to_cpu(eu_stats->info2); ppdu_info->mpdu_fcs_ok_bitmap[4] = __le32_to_cpu(eu_stats->info3); @@ -724,40 +1624,36 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, break; case HAL_PHYRX_RSSI_LEGACY: { - struct hal_rx_phyrx_rssi_legacy_info *rssi = - (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data; - u32 reception_type = 0; - u32 rssi_legacy_info = __le32_to_cpu(rssi->rsvd[0]); + const struct hal_rx_phyrx_rssi_legacy_info *rssi = tlv_data; info[0] = __le32_to_cpu(rssi->info0); + info[1] = __le32_to_cpu(rssi->info1); /* TODO: Please note that the combined rssi will not be accurate * in MU case. Rssi in MU needs to be retrieved from * PHYRX_OTHER_RECEIVE_INFO TLV. */ ppdu_info->rssi_comb = - u32_get_bits(info[0], - HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RSSI_COMB); - reception_type = - u32_get_bits(rssi_legacy_info, - HAL_RX_PHYRX_RSSI_LEGACY_INFO_RSVD1_RECEPTION); - - switch (reception_type) { - case HAL_RECEPTION_TYPE_ULOFMDA: - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; - break; - case HAL_RECEPTION_TYPE_ULMIMO: - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; - break; - default: - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; - break; - } + u32_get_bits(info[1], + HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB); + + ppdu_info->bw = u32_get_bits(info[0], + HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RX_BW); break; } + case HAL_PHYRX_OTHER_RECEIVE_INFO: { + const struct hal_phyrx_common_user_info *cmn_usr_info = tlv_data; + + ppdu_info->gi = le32_get_bits(cmn_usr_info->info0, + HAL_RX_PHY_CMN_USER_INFO0_GI); + break; + } + case HAL_RX_PPDU_START_USER_INFO: + ath12k_dp_mon_hal_rx_parse_user_info(tlv_data, userid, ppdu_info); + break; + case HAL_RXPCU_PPDU_END_INFO: { - struct hal_rx_ppdu_end_duration *ppdu_rx_duration = - (struct hal_rx_ppdu_end_duration *)tlv_data; + const struct hal_rx_ppdu_end_duration *ppdu_rx_duration = tlv_data; info[0] = __le32_to_cpu(ppdu_rx_duration->info0); ppdu_info->rx_duration = @@ -768,9 +1664,7 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, break; } case HAL_RX_MPDU_START: { - struct hal_rx_mpdu_start *mpdu_start = - (struct hal_rx_mpdu_start *)tlv_data; - struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; + const struct hal_rx_mpdu_start *mpdu_start = tlv_data; u16 peer_id; info[1] = __le32_to_cpu(mpdu_start->info1); @@ -783,70 +1677,39 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, if (userid < HAL_MAX_UL_MU_USERS) { info[0] = __le32_to_cpu(mpdu_start->info0); ppdu_info->userid = userid; - ppdu_info->ampdu_id[userid] = - u32_get_bits(info[0], HAL_RX_MPDU_START_INFO1_PEERID); + ppdu_info->userstats[userid].ampdu_id = + u32_get_bits(info[0], HAL_RX_MPDU_START_INFO0_PPDU_ID); } - mon_mpdu = kzalloc(sizeof(*mon_mpdu), GFP_ATOMIC); - if (!mon_mpdu) - return HAL_RX_MON_STATUS_PPDU_NOT_DONE; - - break; + return HAL_RX_MON_STATUS_MPDU_START; } case HAL_RX_MSDU_START: /* TODO: add msdu start parsing logic */ break; - case HAL_MON_BUF_ADDR: { - struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring; - struct dp_mon_packet_info *packet_info = - (struct dp_mon_packet_info *)tlv_data; - int buf_id = u32_get_bits(packet_info->cookie, - DP_RXDMA_BUF_COOKIE_BUF_ID); - struct sk_buff *msdu; - struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; - struct ath12k_skb_rxcb *rxcb; - - spin_lock_bh(&buf_ring->idr_lock); - msdu = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!msdu)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - return HAL_RX_MON_STATUS_PPDU_NOT_DONE; + case HAL_MON_BUF_ADDR: + return HAL_RX_MON_STATUS_BUF_ADDR; + case HAL_RX_MSDU_END: + ath12k_dp_mon_parse_status_msdu_end(pmon, tlv_data); + return HAL_RX_MON_STATUS_MSDU_END; + case HAL_RX_MPDU_END: + return HAL_RX_MON_STATUS_MPDU_END; + case HAL_PHYRX_GENERIC_U_SIG: + ath12k_dp_mon_hal_rx_parse_u_sig_hdr(tlv_data, ppdu_info); + break; + case HAL_PHYRX_GENERIC_EHT_SIG: + /* Handle the case where aggregation is in progress + * or the current TLV is one of the TLVs which should be + * aggregated + */ + if (!ppdu_info->tlv_aggr.in_progress) { + ppdu_info->tlv_aggr.in_progress = true; + ppdu_info->tlv_aggr.tlv_tag = tlv_tag; + ppdu_info->tlv_aggr.cur_len = 0; } - rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(ab->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - - if (mon_mpdu->tail) - mon_mpdu->tail->next = msdu; - else - mon_mpdu->tail = msdu; + ppdu_info->is_eht = true; - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - - break; - } - case HAL_RX_MSDU_END: { - struct rx_msdu_end_qcn9274 *msdu_end = - (struct rx_msdu_end_qcn9274 *)tlv_data; - bool is_first_msdu_in_mpdu; - u16 msdu_end_info; - - msdu_end_info = __le16_to_cpu(msdu_end->info5); - is_first_msdu_in_mpdu = u32_get_bits(msdu_end_info, - RX_MSDU_END_INFO5_FIRST_MSDU); - if (is_first_msdu_in_mpdu) { - pmon->mon_mpdu->head = pmon->mon_mpdu->tail; - pmon->mon_mpdu->tail = NULL; - } - break; - } - case HAL_RX_MPDU_END: - list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list); + ath12k_dp_mon_hal_aggr_tlv(ppdu_info, tlv_len, tlv_data); break; case HAL_DUMMY: return HAL_RX_MON_STATUS_BUF_DONE; @@ -860,59 +1723,323 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, return HAL_RX_MON_STATUS_PPDU_NOT_DONE; } -static void ath12k_dp_mon_rx_msdus_set_payload(struct ath12k *ar, struct sk_buff *msdu) +static void +ath12k_dp_mon_fill_rx_stats_info(struct ath12k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct ieee80211_rx_status *rx_status) +{ + u32 center_freq = ppdu_info->freq; + + rx_status->freq = center_freq; + rx_status->bw = ath12k_mac_bw_to_mac80211_bw(ppdu_info->bw); + rx_status->nss = ppdu_info->nss; + rx_status->rate_idx = 0; + rx_status->encoding = RX_ENC_LEGACY; + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + if (center_freq >= ATH12K_MIN_6GHZ_FREQ && + center_freq <= ATH12K_MAX_6GHZ_FREQ) { + rx_status->band = NL80211_BAND_6GHZ; + } else if (center_freq >= ATH12K_MIN_2GHZ_FREQ && + center_freq <= ATH12K_MAX_2GHZ_FREQ) { + rx_status->band = NL80211_BAND_2GHZ; + } else if (center_freq >= ATH12K_MIN_5GHZ_FREQ && + center_freq <= ATH12K_MAX_5GHZ_FREQ) { + rx_status->band = NL80211_BAND_5GHZ; + } else { + rx_status->band = NUM_NL80211_BANDS; + } +} + +static struct sk_buff +*ath12k_dp_rx_alloc_mon_status_buf(struct ath12k_base *ab, + struct dp_rxdma_mon_ring *rx_ring, + int *buf_id) +{ + struct sk_buff *skb; + dma_addr_t paddr; + + skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE); + + if (!skb) + goto fail_alloc_skb; + + if (!IS_ALIGNED((unsigned long)skb->data, + RX_MON_STATUS_BUF_ALIGN)) { + skb_pull(skb, PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, paddr))) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + *buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max, GFP_ATOMIC); + spin_unlock_bh(&rx_ring->idr_lock); + if (*buf_id < 0) + goto fail_dma_unmap; + + ATH12K_SKB_RXCB(skb)->paddr = paddr; + return skb; + +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); +fail_alloc_skb: + return NULL; +} + +static enum dp_mon_status_buf_state +ath12k_dp_rx_mon_buf_done(struct ath12k_base *ab, struct hal_srng *srng, + struct dp_rxdma_mon_ring *rx_ring) { - u32 rx_pkt_offset, l2_hdr_offset; + struct ath12k_skb_rxcb *rxcb; + struct hal_tlv_64_hdr *tlv; + struct sk_buff *skb; + void *status_desc; + dma_addr_t paddr; + u32 cookie; + int buf_id; + u8 rbm; + + status_desc = ath12k_hal_srng_src_next_peek(ab, srng); + if (!status_desc) + return DP_MON_STATUS_NO_DMA; + + ath12k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm); + + buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + if (!skb) + return DP_MON_STATUS_NO_DMA; + + rxcb = ATH12K_SKB_RXCB(skb); + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + tlv = (struct hal_tlv_64_hdr *)skb->data; + if (le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG) != HAL_RX_STATUS_BUFFER_DONE) + return DP_MON_STATUS_NO_DMA; + + return DP_MON_STATUS_REPLINISH; +} + +static u32 ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id) +{ + u32 ret = 0; + + if ((*ppdu_id < msdu_ppdu_id) && + ((msdu_ppdu_id - *ppdu_id) < DP_NOT_PPDU_ID_WRAP_AROUND)) { + /* Hold on mon dest ring, and reap mon status ring. */ + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } else if ((*ppdu_id > msdu_ppdu_id) && + ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) { + /* PPDU ID has exceeded the maximum value and will + * restart from 0. + */ + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } + return ret; +} + +static +void ath12k_dp_mon_next_link_desc_get(struct hal_rx_msdu_link *msdu_link, + dma_addr_t *paddr, u32 *sw_cookie, u8 *rbm, + struct ath12k_buffer_addr **pp_buf_addr_info) +{ + struct ath12k_buffer_addr *buf_addr_info; + + buf_addr_info = &msdu_link->buf_addr_info; + + ath12k_hal_rx_buf_addr_info_get(buf_addr_info, paddr, sw_cookie, rbm); + + *pp_buf_addr_info = buf_addr_info; +} + +static void +ath12k_dp_mon_fill_rx_rate(struct ath12k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_supported_band *sband; + enum rx_msdu_start_pkt_type pkt_type; + u8 rate_mcs, nss, sgi; + bool is_cck; + + pkt_type = ppdu_info->preamble_type; + rate_mcs = ppdu_info->rate; + nss = ppdu_info->nss; + sgi = ppdu_info->gi; + + switch (pkt_type) { + case RX_MSDU_START_PKT_TYPE_11A: + case RX_MSDU_START_PKT_TYPE_11B: + is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); + if (rx_status->band < NUM_NL80211_BANDS) { + sband = &ar->mac.sbands[rx_status->band]; + rx_status->rate_idx = ath12k_mac_hw_rate_to_idx(sband, rate_mcs, + is_cck); + } + break; + case RX_MSDU_START_PKT_TYPE_11N: + rx_status->encoding = RX_ENC_HT; + if (rate_mcs > ATH12K_HT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in HT mode %d\n", + rate_mcs); + break; + } + rx_status->rate_idx = rate_mcs + (8 * (nss - 1)); + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + break; + case RX_MSDU_START_PKT_TYPE_11AC: + rx_status->encoding = RX_ENC_VHT; + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH12K_VHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in VHT mode %d\n", + rate_mcs); + break; + } + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + break; + case RX_MSDU_START_PKT_TYPE_11AX: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH12K_HE_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in HE mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_HE; + rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); + break; + case RX_MSDU_START_PKT_TYPE_11BE: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in EHT mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_EHT; + rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); + break; + default: + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "monitor receives invalid preamble type %d", + pkt_type); + break; + } +} + +static void ath12k_dp_mon_rx_msdus_set_payload(struct ath12k *ar, + struct sk_buff *head_msdu, + struct sk_buff *tail_msdu) +{ + u32 rx_pkt_offset, l2_hdr_offset, total_offset; rx_pkt_offset = ar->ab->hal.hal_desc_sz; - l2_hdr_offset = ath12k_dp_rx_h_l3pad(ar->ab, - (struct hal_rx_desc *)msdu->data); - skb_pull(msdu, rx_pkt_offset + l2_hdr_offset); + l2_hdr_offset = + ath12k_dp_rx_h_l3pad(ar->ab, (struct hal_rx_desc *)tail_msdu->data); + + if (ar->ab->hw_params->rxdma1_enable) + total_offset = ATH12K_MON_RX_PKT_OFFSET; + else + total_offset = rx_pkt_offset + l2_hdr_offset; + + skb_pull(head_msdu, total_offset); } static struct sk_buff * ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, - u32 mac_id, struct sk_buff *head_msdu, - struct ieee80211_rx_status *rxs, bool *fcs_err) + struct dp_mon_mpdu *mon_mpdu, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct ieee80211_rx_status *rxs) { struct ath12k_base *ab = ar->ab; - struct sk_buff *msdu, *mpdu_buf, *prev_buf; + struct sk_buff *msdu, *mpdu_buf, *prev_buf, *head_frag_list; + struct sk_buff *head_msdu, *tail_msdu; struct hal_rx_desc *rx_desc; - u8 *hdr_desc, *dest, decap_format; + u8 *hdr_desc, *dest, decap_format = mon_mpdu->decap_format; struct ieee80211_hdr_3addr *wh; - u32 err_bitmap; + struct ieee80211_channel *channel; + u32 frag_list_sum_len = 0; + u8 channel_num = ppdu_info->chan_num; mpdu_buf = NULL; + head_msdu = mon_mpdu->head; + tail_msdu = mon_mpdu->tail; - if (!head_msdu) + if (!head_msdu || !tail_msdu) goto err_merge_fail; - rx_desc = (struct hal_rx_desc *)head_msdu->data; - err_bitmap = ath12k_dp_rx_h_mpdu_err(ab, rx_desc); + ath12k_dp_mon_fill_rx_stats_info(ar, ppdu_info, rxs); + + if (unlikely(rxs->band == NUM_NL80211_BANDS || + !ath12k_ar_to_hw(ar)->wiphy->bands[rxs->band])) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "sband is NULL for status band %d channel_num %d center_freq %d pdev_id %d\n", + rxs->band, channel_num, ppdu_info->freq, ar->pdev_idx); - if (err_bitmap & HAL_RX_MPDU_ERR_FCS) - *fcs_err = true; + spin_lock_bh(&ar->data_lock); + channel = ar->rx_channel; + if (channel) { + rxs->band = channel->band; + channel_num = + ieee80211_frequency_to_channel(channel->center_freq); + } + spin_unlock_bh(&ar->data_lock); + } - decap_format = ath12k_dp_rx_h_decap_type(ab, rx_desc); + if (rxs->band < NUM_NL80211_BANDS) + rxs->freq = ieee80211_channel_to_frequency(channel_num, + rxs->band); - ath12k_dp_rx_h_ppdu(ar, rx_desc, rxs); + ath12k_dp_mon_fill_rx_rate(ar, ppdu_info, rxs); if (decap_format == DP_RX_DECAP_TYPE_RAW) { - ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu); + ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu); prev_buf = head_msdu; msdu = head_msdu->next; + head_frag_list = NULL; while (msdu) { - ath12k_dp_mon_rx_msdus_set_payload(ar, msdu); + ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu); + + if (!head_frag_list) + head_frag_list = msdu; + frag_list_sum_len += msdu->len; prev_buf = msdu; msdu = msdu->next; } prev_buf->next = NULL; - skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN); + skb_trim(prev_buf, prev_buf->len); + if (head_frag_list) { + skb_shinfo(head_msdu)->frag_list = head_frag_list; + head_msdu->data_len = frag_list_sum_len; + head_msdu->len += head_msdu->data_len; + head_msdu->next = NULL; + } } else if (decap_format == DP_RX_DECAP_TYPE_NATIVE_WIFI) { u8 qos_pkt = 0; @@ -929,7 +2056,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, msdu = head_msdu; while (msdu) { - ath12k_dp_mon_rx_msdus_set_payload(ar, msdu); + ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu); if (qos_pkt) { dest = skb_push(msdu, sizeof(__le16)); if (!dest) @@ -944,7 +2071,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, goto err_merge_fail; ath12k_dbg(ab, ATH12K_DBG_DATA, - "mpdu_buf %pK mpdu_buf->len %u", + "mpdu_buf %p mpdu_buf->len %u", prev_buf, prev_buf->len); } else { ath12k_dbg(ab, ATH12K_DBG_DATA, @@ -958,7 +2085,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, err_merge_fail: if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { ath12k_dbg(ab, ATH12K_DBG_DATA, - "err_merge_fail mpdu_buf %pK", mpdu_buf); + "err_merge_fail mpdu_buf %p", mpdu_buf); /* Free the head buffer */ dev_kfree_skb_any(mpdu_buf); } @@ -1020,18 +2147,70 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, { struct ieee80211_supported_band *sband; u8 *ptr = NULL; - u16 ampdu_id = ppduinfo->ampdu_id[ppduinfo->userid]; rxs->flag |= RX_FLAG_MACTIME_START; rxs->signal = ppduinfo->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR; rxs->nss = ppduinfo->nss + 1; - if (ampdu_id) { + if (ppduinfo->userstats[ppduinfo->userid].ampdu_present) { rxs->flag |= RX_FLAG_AMPDU_DETAILS; - rxs->ampdu_reference = ampdu_id; + rxs->ampdu_reference = ppduinfo->userstats[ppduinfo->userid].ampdu_id; } - if (ppduinfo->he_mu_flags) { + if (ppduinfo->is_eht || ppduinfo->eht_usig) { + struct ieee80211_radiotap_tlv *tlv; + struct ieee80211_radiotap_eht *eht; + struct ieee80211_radiotap_eht_usig *usig; + u16 len = 0, i, eht_len, usig_len; + u8 user; + + if (ppduinfo->is_eht) { + eht_len = struct_size(eht, + user_info, + ppduinfo->eht_info.num_user_info); + len += sizeof(*tlv) + eht_len; + } + + if (ppduinfo->eht_usig) { + usig_len = sizeof(*usig); + len += sizeof(*tlv) + usig_len; + } + + rxs->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + rxs->encoding = RX_ENC_EHT; + + skb_reset_mac_header(mon_skb); + + tlv = skb_push(mon_skb, len); + + if (ppduinfo->is_eht) { + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT); + tlv->len = cpu_to_le16(eht_len); + + eht = (struct ieee80211_radiotap_eht *)tlv->data; + eht->known = ppduinfo->eht_info.eht.known; + + for (i = 0; + i < ARRAY_SIZE(eht->data) && + i < ARRAY_SIZE(ppduinfo->eht_info.eht.data); + i++) + eht->data[i] = ppduinfo->eht_info.eht.data[i]; + + for (user = 0; user < ppduinfo->eht_info.num_user_info; user++) + put_unaligned_le32(ppduinfo->eht_info.user_info[user], + &eht->user_info[user]); + + tlv = (struct ieee80211_radiotap_tlv *)&tlv->data[eht_len]; + } + + if (ppduinfo->eht_usig) { + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT_USIG); + tlv->len = cpu_to_le16(usig_len); + + usig = (struct ieee80211_radiotap_eht_usig *)tlv->data; + *usig = ppduinfo->u_sig_info.usig; + } + } else if (ppduinfo->he_mu_flags) { rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he_mu)); @@ -1060,7 +2239,8 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *napi, struct sk_buff *msdu, - struct ieee80211_rx_status *status) + struct ieee80211_rx_status *status, + u8 decap) { static const struct ieee80211_radiotap_he known = { .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | @@ -1072,10 +2252,12 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct struct ieee80211_sta *pubsta = NULL; struct ath12k_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - u8 decap = DP_RX_DECAP_TYPE_RAW; + struct ath12k_dp_rx_info rx_info; bool is_mcbc = rxcb->is_mcbc; bool is_eapol_tkip = rxcb->is_eapol; + status->link_valid = 0; + if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) && !(status->flag & RX_FLAG_SKIP_MONITOR)) { he = skb_push(msdu, sizeof(known)); @@ -1083,16 +2265,21 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct status->flag |= RX_FLAG_RADIOTAP_HE; } - if (!(status->flag & RX_FLAG_ONLY_MONITOR)) - decap = ath12k_dp_rx_h_decap_type(ar->ab, rxcb->rx_desc); spin_lock_bh(&ar->ab->base_lock); - peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu); - if (peer && peer->sta) + rx_info.addr2_present = false; + peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, &rx_info); + if (peer && peer->sta) { pubsta = peer->sta; + if (pubsta->valid_links) { + status->link_valid = 1; + status->link_id = peer->link_id; + } + } + spin_unlock_bh(&ar->ab->base_lock); ath12k_dbg(ar->ab, ATH12K_DBG_DATA, - "rx skb %pK len %u peer %pM %u %s %s%s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %p len %u peer %pM %u %s %s%s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", msdu, msdu->len, peer ? peer->addr : NULL, @@ -1134,25 +2321,24 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } -static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, - struct sk_buff *head_msdu, +static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, + struct dp_mon_mpdu *mon_mpdu, struct hal_rx_mon_ppdu_info *ppduinfo, struct napi_struct *napi) { struct ath12k_pdev_dp *dp = &ar->dp; struct sk_buff *mon_skb, *skb_next, *header; struct ieee80211_rx_status *rxs = &dp->rx_status; - bool fcs_err = false; + u8 decap = DP_RX_DECAP_TYPE_RAW; - mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mac_id, head_msdu, - rxs, &fcs_err); + mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mon_mpdu, ppduinfo, rxs); if (!mon_skb) goto mon_deliver_fail; header = mon_skb; rxs->flag = 0; - if (fcs_err) + if (mon_mpdu->err_bitmap & HAL_RX_MPDU_ERR_FCS) rxs->flag = RX_FLAG_FAILED_FCS_CRC; do { @@ -1169,8 +2355,12 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, rxs->flag |= RX_FLAG_ALLOW_SAME_PN; } rxs->flag |= RX_FLAG_ONLY_MONITOR; + + if (!(rxs->flag & RX_FLAG_ONLY_MONITOR)) + decap = mon_mpdu->decap_format; + ath12k_dp_mon_update_radiotap(ar, ppduinfo, mon_skb, rxs); - ath12k_dp_mon_rx_deliver_msdu(ar, napi, mon_skb, rxs); + ath12k_dp_mon_rx_deliver_msdu(ar, napi, mon_skb, rxs, decap); mon_skb = skb_next; } while (mon_skb); rxs->flag = 0; @@ -1178,7 +2368,7 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, return 0; mon_deliver_fail: - mon_skb = head_msdu; + mon_skb = mon_mpdu->head; while (mon_skb) { skb_next = mon_skb->next; dev_kfree_skb_any(mon_skb); @@ -1187,25 +2377,146 @@ mon_deliver_fail: return -EINVAL; } +static int ath12k_dp_pkt_set_pktlen(struct sk_buff *skb, u32 len) +{ + if (skb->len > len) { + skb_trim(skb, len); + } else { + if (skb_tailroom(skb) < len - skb->len) { + if ((pskb_expand_head(skb, 0, + len - skb->len - skb_tailroom(skb), + GFP_ATOMIC))) { + return -ENOMEM; + } + } + skb_put(skb, (len - skb->len)); + } + + return 0; +} + +/* Hardware fill buffer with 128 bytes aligned. So need to reap it + * with 128 bytes aligned. + */ +#define RXDMA_DATA_DMA_BLOCK_SIZE 128 + +static void +ath12k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info, + bool *is_frag, u32 *total_len, + u32 *frag_len, u32 *msdu_cnt) +{ + if (info->msdu_flags & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION) { + *is_frag = true; + *frag_len = (RX_MON_STATUS_BASE_BUF_SIZE - + sizeof(struct hal_rx_desc)) & + ~(RXDMA_DATA_DMA_BLOCK_SIZE - 1); + *total_len += *frag_len; + } else { + if (*is_frag) + *frag_len = info->msdu_len - *total_len; + else + *frag_len = info->msdu_len; + + *msdu_cnt -= 1; + } +} + +static int +ath12k_dp_mon_parse_status_buf(struct ath12k *ar, + struct ath12k_mon_data *pmon, + const struct dp_mon_packet_info *packet_info) +{ + struct ath12k_base *ab = ar->ab; + struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring; + struct sk_buff *msdu; + int buf_id; + u32 offset; + + buf_id = u32_get_bits(packet_info->cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); + + spin_lock_bh(&buf_ring->idr_lock); + msdu = idr_remove(&buf_ring->bufs_idr, buf_id); + spin_unlock_bh(&buf_ring->idr_lock); + + if (unlikely(!msdu)) { + ath12k_warn(ab, "mon dest desc with inval buf_id %d\n", buf_id); + return 0; + } + + dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(msdu)->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + offset = packet_info->dma_length + ATH12K_MON_RX_DOT11_OFFSET; + if (ath12k_dp_pkt_set_pktlen(msdu, offset)) { + dev_kfree_skb_any(msdu); + goto dest_replenish; + } + + if (!pmon->mon_mpdu->head) + pmon->mon_mpdu->head = msdu; + else + pmon->mon_mpdu->tail->next = msdu; + + pmon->mon_mpdu->tail = msdu; + +dest_replenish: + ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); + + return 0; +} + +static int +ath12k_dp_mon_parse_rx_dest_tlv(struct ath12k *ar, + struct ath12k_mon_data *pmon, + enum hal_rx_mon_status hal_status, + const void *tlv_data) +{ + switch (hal_status) { + case HAL_RX_MON_STATUS_MPDU_START: + if (WARN_ON_ONCE(pmon->mon_mpdu)) + break; + + pmon->mon_mpdu = kzalloc(sizeof(*pmon->mon_mpdu), GFP_ATOMIC); + if (!pmon->mon_mpdu) + return -ENOMEM; + break; + case HAL_RX_MON_STATUS_BUF_ADDR: + return ath12k_dp_mon_parse_status_buf(ar, pmon, tlv_data); + case HAL_RX_MON_STATUS_MPDU_END: + /* If no MSDU then free empty MPDU */ + if (pmon->mon_mpdu->tail) { + pmon->mon_mpdu->tail->next = NULL; + list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list); + } else { + kfree(pmon->mon_mpdu); + } + pmon->mon_mpdu = NULL; + break; + case HAL_RX_MON_STATUS_MSDU_END: + pmon->mon_mpdu->decap_format = pmon->decap_format; + pmon->mon_mpdu->err_bitmap = pmon->err_bitmap; + break; + default: + break; + } + + return 0; +} + static enum hal_rx_mon_status -ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon, +ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, struct sk_buff *skb) { - struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; - struct hal_tlv_hdr *tlv; + struct hal_tlv_64_hdr *tlv; + struct ath12k_skb_rxcb *rxcb; enum hal_rx_mon_status hal_status; - u32 tlv_userid = 0; u16 tlv_tag, tlv_len; u8 *ptr = skb->data; - memset(ppdu_info, 0, sizeof(struct hal_rx_mon_ppdu_info)); - do { - tlv = (struct hal_tlv_hdr *)ptr; - tlv_tag = le32_get_bits(tlv->tl, HAL_TLV_HDR_TAG); - tlv_len = le32_get_bits(tlv->tl, HAL_TLV_HDR_LEN); - tlv_userid = le32_get_bits(tlv->tl, HAL_TLV_USR_ID); - ptr += sizeof(*tlv); + tlv = (struct hal_tlv_64_hdr *)ptr; + tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); /* The actual length of PPDU_END is the combined length of many PHY * TLVs that follow. Skip the TLV header and @@ -1215,16 +2526,30 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon if (tlv_tag == HAL_RX_PPDU_END) tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + else + tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); - hal_status = ath12k_dp_mon_rx_parse_status_tlv(ab, pmon, - tlv_tag, ptr, tlv_userid); - ptr += tlv_len; - ptr = PTR_ALIGN(ptr, HAL_TLV_ALIGN); + hal_status = ath12k_dp_mon_rx_parse_status_tlv(ar, pmon, tlv); + + if (ar->monitor_started && ar->ab->hw_params->rxdma1_enable && + ath12k_dp_mon_parse_rx_dest_tlv(ar, pmon, hal_status, tlv->value)) + return HAL_RX_MON_STATUS_PPDU_DONE; + + ptr += sizeof(*tlv) + tlv_len; + ptr = PTR_ALIGN(ptr, HAL_TLV_64_ALIGN); - if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE) + if ((ptr - skb->data) > skb->len) break; - } while (hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE); + } while ((hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE) || + (hal_status == HAL_RX_MON_STATUS_BUF_ADDR) || + (hal_status == HAL_RX_MON_STATUS_MPDU_START) || + (hal_status == HAL_RX_MON_STATUS_MPDU_END) || + (hal_status == HAL_RX_MON_STATUS_MSDU_END)); + + rxcb = ATH12K_SKB_RXCB(skb); + if (rxcb->is_end_of_ppdu) + hal_status = HAL_RX_MON_STATUS_PPDU_DONE; return hal_status; } @@ -1232,31 +2557,27 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon enum hal_rx_mon_status ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi) { - struct ath12k_base *ab = ar->ab; struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; struct dp_mon_mpdu *tmp; struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; - struct sk_buff *head_msdu, *tail_msdu; - enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; + enum hal_rx_mon_status hal_status; - ath12k_dp_mon_parse_rx_dest(ab, pmon, skb); + hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); + if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) + return hal_status; list_for_each_entry_safe(mon_mpdu, tmp, &pmon->dp_rx_mon_mpdu_list, list) { list_del(&mon_mpdu->list); - head_msdu = mon_mpdu->head; - tail_msdu = mon_mpdu->tail; - if (head_msdu && tail_msdu) { - ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, - ppdu_info, napi); - } + if (mon_mpdu->head && mon_mpdu->tail) + ath12k_dp_mon_rx_deliver(ar, mon_mpdu, ppdu_info, napi); kfree(mon_mpdu); } + return hal_status; } @@ -1335,6 +2656,94 @@ fail_alloc_skb: return -ENOMEM; } +int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab, + struct dp_rxdma_mon_ring *rx_ring, + int req_entries) +{ + enum hal_rx_buf_return_buf_manager mgr = + ab->hw_params->hal_params->rx_buf_rbm; + int num_free, num_remain, buf_id; + struct ath12k_buffer_addr *desc; + struct hal_srng *srng; + struct sk_buff *skb; + dma_addr_t paddr; + u32 cookie; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath12k_hal_srng_access_begin(ab, srng); + + num_free = ath12k_hal_srng_src_num_free(ab, srng, true); + if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4)) + req_entries = num_free; + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE); + if (!skb) + break; + + if (!IS_ALIGNED((unsigned long)skb->data, + RX_MON_STATUS_BUF_ALIGN)) { + skb_pull(skb, + PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max * 3, GFP_ATOMIC); + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id < 0) + goto fail_dma_unmap; + cookie = u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID); + + desc = ath12k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_buf_unassign; + + ATH12K_SKB_RXCB(skb)->paddr = paddr; + + num_remain--; + + ath12k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath12k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_buf_unassign: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); + + ath12k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + static struct dp_mon_tx_ppdu_info * ath12k_dp_mon_tx_get_ppdu_info(struct ath12k_mon_data *pmon, unsigned int ppdu_id, @@ -1596,7 +3005,7 @@ ath12k_dp_mon_tx_gen_prot_frame(struct dp_mon_tx_ppdu_info *tx_ppdu_info) static enum dp_mon_tx_tlv_status ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, struct ath12k_mon_data *pmon, - u16 tlv_tag, u8 *tlv_data, u32 userid) + u16 tlv_tag, const void *tlv_data, u32 userid) { struct dp_mon_tx_ppdu_info *tx_ppdu_info; enum dp_mon_tx_tlv_status status = DP_MON_TX_STATUS_PPDU_NOT_DONE; @@ -1606,8 +3015,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, switch (tlv_tag) { case HAL_TX_FES_SETUP: { - struct hal_tx_fes_setup *tx_fes_setup = - (struct hal_tx_fes_setup *)tlv_data; + const struct hal_tx_fes_setup *tx_fes_setup = tlv_data; info[0] = __le32_to_cpu(tx_fes_setup->info0); tx_ppdu_info->ppdu_id = __le32_to_cpu(tx_fes_setup->schedule_id); @@ -1618,8 +3026,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_TX_FES_STATUS_END: { - struct hal_tx_fes_status_end *tx_fes_status_end = - (struct hal_tx_fes_status_end *)tlv_data; + const struct hal_tx_fes_status_end *tx_fes_status_end = tlv_data; u32 tst_15_0, tst_31_16; info[0] = __le32_to_cpu(tx_fes_status_end->info0); @@ -1636,8 +3043,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_RX_RESPONSE_REQUIRED_INFO: { - struct hal_rx_resp_req_info *rx_resp_req_info = - (struct hal_rx_resp_req_info *)tlv_data; + const struct hal_rx_resp_req_info *rx_resp_req_info = tlv_data; u32 addr_32; u16 addr_16; @@ -1682,8 +3088,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_PCU_PPDU_SETUP_INIT: { - struct hal_tx_pcu_ppdu_setup_init *ppdu_setup = - (struct hal_tx_pcu_ppdu_setup_init *)tlv_data; + const struct hal_tx_pcu_ppdu_setup_init *ppdu_setup = tlv_data; u32 addr_32; u16 addr_16; @@ -1729,8 +3134,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_TX_QUEUE_EXTENSION: { - struct hal_tx_queue_exten *tx_q_exten = - (struct hal_tx_queue_exten *)tlv_data; + const struct hal_tx_queue_exten *tx_q_exten = tlv_data; info[0] = __le32_to_cpu(tx_q_exten->info0); @@ -1742,8 +3146,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_TX_FES_STATUS_START: { - struct hal_tx_fes_status_start *tx_fes_start = - (struct hal_tx_fes_status_start *)tlv_data; + const struct hal_tx_fes_status_start *tx_fes_start = tlv_data; info[0] = __le32_to_cpu(tx_fes_start->info0); @@ -1754,8 +3157,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_TX_FES_STATUS_PROT: { - struct hal_tx_fes_status_prot *tx_fes_status = - (struct hal_tx_fes_status_prot *)tlv_data; + const struct hal_tx_fes_status_prot *tx_fes_status = tlv_data; u32 start_timestamp; u32 end_timestamp; @@ -1782,8 +3184,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, case HAL_TX_FES_STATUS_START_PPDU: case HAL_TX_FES_STATUS_START_PROT: { - struct hal_tx_fes_status_start_prot *tx_fes_stat_start = - (struct hal_tx_fes_status_start_prot *)tlv_data; + const struct hal_tx_fes_status_start_prot *tx_fes_stat_start = tlv_data; u64 ppdu_ts; info[0] = __le32_to_cpu(tx_fes_stat_start->info0); @@ -1798,8 +3199,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_TX_FES_STATUS_USER_PPDU: { - struct hal_tx_fes_status_user_ppdu *tx_fes_usr_ppdu = - (struct hal_tx_fes_status_user_ppdu *)tlv_data; + const struct hal_tx_fes_status_user_ppdu *tx_fes_usr_ppdu = tlv_data; info[0] = __le32_to_cpu(tx_fes_usr_ppdu->info0); @@ -1842,8 +3242,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, break; case HAL_RX_FRAME_BITMAP_ACK: { - struct hal_rx_frame_bitmap_ack *fbm_ack = - (struct hal_rx_frame_bitmap_ack *)tlv_data; + const struct hal_rx_frame_bitmap_ack *fbm_ack = tlv_data; u32 addr_32; u16 addr_16; @@ -1861,8 +3260,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, } case HAL_MACTX_PHY_DESC: { - struct hal_tx_phy_desc *tx_phy_desc = - (struct hal_tx_phy_desc *)tlv_data; + const struct hal_tx_phy_desc *tx_phy_desc = tlv_data; info[0] = __le32_to_cpu(tx_phy_desc->info0); info[1] = __le32_to_cpu(tx_phy_desc->info1); @@ -1903,43 +3301,6 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, break; } - case HAL_MON_BUF_ADDR: { - struct dp_rxdma_mon_ring *buf_ring = &ab->dp.tx_mon_buf_ring; - struct dp_mon_packet_info *packet_info = - (struct dp_mon_packet_info *)tlv_data; - int buf_id = u32_get_bits(packet_info->cookie, - DP_RXDMA_BUF_COOKIE_BUF_ID); - struct sk_buff *msdu; - struct dp_mon_mpdu *mon_mpdu = tx_ppdu_info->tx_mon_mpdu; - struct ath12k_skb_rxcb *rxcb; - - spin_lock_bh(&buf_ring->idr_lock); - msdu = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!msdu)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - return DP_MON_TX_STATUS_PPDU_NOT_DONE; - } - - rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(ab->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - - if (!mon_mpdu->head) - mon_mpdu->head = msdu; - else if (mon_mpdu->tail) - mon_mpdu->tail->next = msdu; - - mon_mpdu->tail = msdu; - - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - status = DP_MON_TX_BUFFER_ADDR; - break; - } - case HAL_TX_MPDU_END: list_add_tail(&tx_ppdu_info->tx_mon_mpdu->list, &tx_ppdu_info->dp_tx_mon_mpdu_list); @@ -1980,20 +3341,18 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag, } static void -ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, +ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, struct napi_struct *napi, struct dp_mon_tx_ppdu_info *tx_ppdu_info) { struct dp_mon_mpdu *tmp, *mon_mpdu; - struct sk_buff *head_msdu; list_for_each_entry_safe(mon_mpdu, tmp, &tx_ppdu_info->dp_tx_mon_mpdu_list, list) { list_del(&mon_mpdu->list); - head_msdu = mon_mpdu->head; - if (head_msdu) - ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, + if (mon_mpdu->head) + ath12k_dp_mon_rx_deliver(ar, mon_mpdu, &tx_ppdu_info->rx_status, napi); kfree(mon_mpdu); @@ -2003,7 +3362,6 @@ ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, enum hal_rx_mon_status ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi, u32 ppdu_id) @@ -2050,170 +3408,57 @@ ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, break; } while (tlv_status != DP_MON_TX_FES_STATUS_END); - ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_data_ppdu_info); - ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_prot_ppdu_info); + ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_data_ppdu_info); + ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_prot_ppdu_info); return tlv_status; } -int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget, - enum dp_monitor_mode monitor_mode, - struct napi_struct *napi) -{ - struct hal_mon_dest_desc *mon_dst_desc; - struct ath12k_pdev_dp *pdev_dp = &ar->dp; - struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&pdev_dp->mon_data; - struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; - struct sk_buff *skb; - struct ath12k_skb_rxcb *rxcb; - struct dp_srng *mon_dst_ring; - struct hal_srng *srng; - struct dp_rxdma_mon_ring *buf_ring; - u64 cookie; - u32 ppdu_id; - int num_buffs_reaped = 0, srng_id, buf_id; - u8 dest_idx = 0, i; - bool end_of_ppdu; - struct hal_rx_mon_ppdu_info *ppdu_info; - struct ath12k_peer *peer = NULL; - - ppdu_info = &pmon->mon_ppdu_info; - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; - - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); - - if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) { - mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; - buf_ring = &dp->rxdma_mon_buf_ring; - } else { - mon_dst_ring = &pdev_dp->tx_mon_dst_ring[srng_id]; - buf_ring = &dp->tx_mon_buf_ring; - } - - srng = &ab->hal.srng_list[mon_dst_ring->ring_id]; - - spin_lock_bh(&srng->lock); - ath12k_hal_srng_access_begin(ab, srng); - - while (likely(*budget)) { - *budget -= 1; - mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng); - if (unlikely(!mon_dst_desc)) - break; - - cookie = le32_to_cpu(mon_dst_desc->cookie); - buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); - - spin_lock_bh(&buf_ring->idr_lock); - skb = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!skb)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - goto move_next; - } - - rxcb = ATH12K_SKB_RXCB(skb); - dma_unmap_single(ab->dev, rxcb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - - pmon->dest_skb_q[dest_idx] = skb; - dest_idx++; - ppdu_id = le32_to_cpu(mon_dst_desc->ppdu_id); - end_of_ppdu = le32_get_bits(mon_dst_desc->info0, - HAL_MON_DEST_INFO0_END_OF_PPDU); - if (!end_of_ppdu) - continue; - - for (i = 0; i < dest_idx; i++) { - skb = pmon->dest_skb_q[i]; - - if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) - ath12k_dp_mon_rx_parse_mon_status(ar, pmon, mac_id, - skb, napi); - else - ath12k_dp_mon_tx_parse_mon_status(ar, pmon, mac_id, - skb, napi, ppdu_id); - - peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); - - if (!peer || !peer->sta) { - ath12k_dbg(ab, ATH12K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - dev_kfree_skb_any(skb); - continue; - } - - dev_kfree_skb_any(skb); - pmon->dest_skb_q[i] = NULL; - } - - dest_idx = 0; -move_next: - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - ath12k_hal_srng_src_get_next_entry(ab, srng); - num_buffs_reaped++; - } - - ath12k_hal_srng_access_end(ab, srng); - spin_unlock_bh(&srng->lock); - - return num_buffs_reaped; -} - static void ath12k_dp_mon_rx_update_peer_rate_table_stats(struct ath12k_rx_peer_stats *rx_stats, struct hal_rx_mon_ppdu_info *ppdu_info, struct hal_rx_user_status *user_stats, u32 num_msdu) { - u32 rate_idx = 0; + struct ath12k_rx_peer_rate_stats *stats; u32 mcs_idx = (user_stats) ? user_stats->mcs : ppdu_info->mcs; u32 nss_idx = (user_stats) ? user_stats->nss - 1 : ppdu_info->nss - 1; u32 bw_idx = ppdu_info->bw; u32 gi_idx = ppdu_info->gi; + u32 len; - if ((mcs_idx > HAL_RX_MAX_MCS_HE) || (nss_idx >= HAL_RX_MAX_NSS) || - (bw_idx >= HAL_RX_BW_MAX) || (gi_idx >= HAL_RX_GI_MAX)) { + if (mcs_idx > HAL_RX_MAX_MCS_HT || nss_idx >= HAL_RX_MAX_NSS || + bw_idx >= HAL_RX_BW_MAX || gi_idx >= HAL_RX_GI_MAX) { return; } - if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N || - ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC) { - rate_idx = mcs_idx * 8 + 8 * 10 * nss_idx; - rate_idx += bw_idx * 2 + gi_idx; - } else if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX) { + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX || + ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE) gi_idx = ath12k_he_gi_to_nl80211_he_gi(ppdu_info->gi); - rate_idx = mcs_idx * 12 + 12 * 12 * nss_idx; - rate_idx += bw_idx * 3 + gi_idx; - } else { - return; - } - rx_stats->pkt_stats.rx_rate[rate_idx] += num_msdu; + rx_stats->pkt_stats.rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += num_msdu; + stats = &rx_stats->byte_stats; + if (user_stats) - rx_stats->byte_stats.rx_rate[rate_idx] += user_stats->mpdu_ok_byte_count; + len = user_stats->mpdu_ok_byte_count; else - rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len; + len = ppdu_info->mpdu_len; + + stats->rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += len; } static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, - struct ath12k_sta *arsta, + struct ath12k_link_sta *arsta, struct hal_rx_mon_ppdu_info *ppdu_info) { struct ath12k_rx_peer_stats *rx_stats = arsta->rx_stats; u32 num_msdu; + arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); if (!rx_stats) return; - arsta->rssi_comb = ppdu_info->rssi_comb; - num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; @@ -2285,6 +3530,12 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, rx_stats->byte_stats.he_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; } + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE && + ppdu_info->mcs <= HAL_RX_MAX_MCS_BE) { + rx_stats->pkt_stats.be_mcs_count[ppdu_info->mcs] += num_msdu; + rx_stats->byte_stats.be_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; + } + if ((ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A || ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) && ppdu_info->rate < HAL_RX_LEGACY_RATE_INVALID) { @@ -2359,7 +3610,8 @@ ath12k_dp_mon_rx_update_user_stats(struct ath12k *ar, struct hal_rx_mon_ppdu_info *ppdu_info, u32 uid) { - struct ath12k_sta *arsta = NULL; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; struct ath12k_rx_peer_stats *rx_stats = NULL; struct hal_rx_user_status *user_stats = &ppdu_info->userstats[uid]; struct ath12k_peer *peer; @@ -2376,14 +3628,14 @@ ath12k_dp_mon_rx_update_user_stats(struct ath12k *ar, return; } - arsta = ath12k_sta_to_arsta(peer->sta); + ahsta = ath12k_sta_to_ahsta(peer->sta); + arsta = &ahsta->deflink; + arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); rx_stats = arsta->rx_stats; - if (!rx_stats) return; - arsta->rssi_comb = ppdu_info->rssi_comb; - num_msdu = user_stats->tcp_msdu_count + user_stats->tcp_ack_msdu_count + user_stats->udp_msdu_count + user_stats->other_msdu_count; @@ -2469,8 +3721,15 @@ ath12k_dp_mon_rx_update_peer_mu_stats(struct ath12k *ar, ath12k_dp_mon_rx_update_user_stats(ar, ppdu_info, i); } -int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, - struct napi_struct *napi, int *budget) +static void +ath12k_dp_mon_rx_memset_ppdu_info(struct hal_rx_mon_ppdu_info *ppdu_info) +{ + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; +} + +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, + struct napi_struct *napi) { struct ath12k_base *ab = ar->ab; struct ath12k_pdev_dp *pdev_dp = &ar->dp; @@ -2483,15 +3742,17 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, struct dp_srng *mon_dst_ring; struct hal_srng *srng; struct dp_rxdma_mon_ring *buf_ring; - struct ath12k_sta *arsta = NULL; + struct ath12k_sta *ahsta = NULL; + struct ath12k_link_sta *arsta; struct ath12k_peer *peer; + struct sk_buff_head skb_list; u64 cookie; int num_buffs_reaped = 0, srng_id, buf_id; - u8 dest_idx = 0, i; - bool end_of_ppdu; - u32 hal_status; + u32 hal_status, end_offset, info0, end_reason; + u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); + __skb_queue_head_init(&skb_list); + srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx); mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; buf_ring = &dp->rxdma_mon_buf_ring; @@ -2504,6 +3765,15 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng); if (unlikely(!mon_dst_desc)) break; + + /* In case of empty descriptor, the cookie in the ring descriptor + * is invalid. Therefore, this entry is skipped, and ring processing + * continues. + */ + info0 = le32_to_cpu(mon_dst_desc->info0); + if (u32_get_bits(info0, HAL_MON_DEST_INFO0_EMPTY_DESC)) + goto move_next; + cookie = le32_to_cpu(mon_dst_desc->cookie); buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); @@ -2521,62 +3791,583 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, dma_unmap_single(ab->dev, rxcb->paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); - pmon->dest_skb_q[dest_idx] = skb; - dest_idx++; - end_of_ppdu = le32_get_bits(mon_dst_desc->info0, - HAL_MON_DEST_INFO0_END_OF_PPDU); - if (!end_of_ppdu) + + end_reason = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_REASON); + + /* HAL_MON_FLUSH_DETECTED implies that an rx flush received at the end of + * rx PPDU and HAL_MON_PPDU_TRUNCATED implies that the PPDU got + * truncated due to a system level error. In both the cases, buffer data + * can be discarded + */ + if ((end_reason == HAL_MON_FLUSH_DETECTED) || + (end_reason == HAL_MON_PPDU_TRUNCATED)) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "Monitor dest descriptor end reason %d", end_reason); + dev_kfree_skb_any(skb); + goto move_next; + } + + /* Calculate the budget when the ring descriptor with the + * HAL_MON_END_OF_PPDU to ensure that one PPDU worth of data is always + * reaped. This helps to efficiently utilize the NAPI budget. + */ + if (end_reason == HAL_MON_END_OF_PPDU) { + *budget -= 1; + rxcb->is_end_of_ppdu = true; + } + + end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET); + if (likely(end_offset <= DP_RX_BUFFER_SIZE)) { + skb_put(skb, end_offset); + } else { + ath12k_warn(ab, + "invalid offset on mon stats destination %u\n", + end_offset); + skb_put(skb, DP_RX_BUFFER_SIZE); + } + + __skb_queue_tail(&skb_list, skb); + +move_next: + ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); + ath12k_hal_srng_dst_get_next_entry(ab, srng); + num_buffs_reaped++; + } + + ath12k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + if (!num_buffs_reaped) + return 0; + + /* In some cases, one PPDU worth of data can be spread across multiple NAPI + * schedules, To avoid losing existing parsed ppdu_info information, skip + * the memset of the ppdu_info structure and continue processing it. + */ + if (!ppdu_info->ppdu_continuation) + ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info); + + while ((skb = __skb_dequeue(&skb_list))) { + hal_status = ath12k_dp_mon_rx_parse_mon_status(ar, pmon, skb, napi); + if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + ppdu_info->ppdu_continuation = true; + dev_kfree_skb_any(skb); continue; + } + + if (ppdu_info->peer_id == HAL_INVALID_PEERID) + goto free_skb; + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "failed to find the peer with monitor peer_id %d\n", + ppdu_info->peer_id); + goto next_skb; + } + + if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { + ahsta = ath12k_sta_to_ahsta(peer->sta); + arsta = &ahsta->deflink; + ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta, + ppdu_info); + } else if ((ppdu_info->fc_valid) && + (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { + ath12k_dp_mon_rx_process_ulofdma(ppdu_info); + ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info); + } + +next_skb: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +free_skb: + dev_kfree_skb_any(skb); + ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info); + } + + return num_buffs_reaped; +} + +static int ath12k_dp_rx_reap_mon_status_ring(struct ath12k_base *ab, int mac_id, + int *budget, struct sk_buff_head *skb_list) +{ + const struct ath12k_hw_hal_params *hal_params; + int buf_id, srng_id, num_buffs_reaped = 0; + enum dp_mon_status_buf_state reap_status; + struct dp_rxdma_mon_ring *rx_ring; + struct ath12k_mon_data *pmon; + struct ath12k_skb_rxcb *rxcb; + struct hal_tlv_64_hdr *tlv; + void *rx_mon_status_desc; + struct hal_srng *srng; + struct ath12k_dp *dp; + struct sk_buff *skb; + struct ath12k *ar; + dma_addr_t paddr; + u32 cookie; + u8 rbm; + + ar = ab->pdevs[ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id)].ar; + dp = &ab->dp; + pmon = &ar->dp.mon_data; + srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); + rx_ring = &dp->rx_mon_status_refill_ring[srng_id]; + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath12k_hal_srng_access_begin(ab, srng); + + while (*budget) { + *budget -= 1; + rx_mon_status_desc = ath12k_hal_srng_src_peek(ab, srng); + if (!rx_mon_status_desc) { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + break; + } + ath12k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr, + &cookie, &rbm); + if (paddr) { + buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + if (!skb) { + ath12k_warn(ab, "rx monitor status with invalid buf_id %d\n", + buf_id); + pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; + } - for (i = 0; i < dest_idx; i++) { - skb = pmon->dest_skb_q[i]; - hal_status = ath12k_dp_mon_parse_rx_dest(ab, pmon, skb); + rxcb = ATH12K_SKB_RXCB(skb); + + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + tlv = (struct hal_tlv_64_hdr *)skb->data; + if (le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG) != + HAL_RX_STATUS_BUFFER_DONE) { + pmon->buf_state = DP_MON_STATUS_NO_DMA; + ath12k_warn(ab, + "mon status DONE not set %llx, buf_id %d\n", + le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG), + buf_id); + /* RxDMA status done bit might not be set even + * though tp is moved by HW. + */ + + /* If done status is missing: + * 1. As per MAC team's suggestion, + * when HP + 1 entry is peeked and if DMA + * is not done and if HP + 2 entry's DMA done + * is set. skip HP + 1 entry and + * start processing in next interrupt. + * 2. If HP + 2 entry's DMA done is not set, + * poll onto HP + 1 entry DMA done to be set. + * Check status for same buffer for next time + * dp_rx_mon_status_srng_process + */ + reap_status = ath12k_dp_rx_mon_buf_done(ab, srng, + rx_ring); + if (reap_status == DP_MON_STATUS_NO_DMA) + continue; + + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); - if (ppdu_info->peer_id == HAL_INVALID_PEERID || - hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { dev_kfree_skb_any(skb); - continue; + pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; } - rcu_read_lock(); - spin_lock_bh(&ab->base_lock); - peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); - if (!peer || !peer->sta) { - ath12k_dbg(ab, ATH12K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + if (ath12k_dp_pkt_set_pktlen(skb, RX_MON_STATUS_BUF_SIZE)) { dev_kfree_skb_any(skb); + goto move_next; + } + __skb_queue_tail(skb_list, skb); + } else { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + } +move_next: + skb = ath12k_dp_rx_alloc_mon_status_buf(ab, rx_ring, + &buf_id); + + if (!skb) { + ath12k_warn(ab, "failed to alloc buffer for status ring\n"); + hal_params = ab->hw_params->hal_params; + ath12k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0, + hal_params->rx_buf_rbm); + num_buffs_reaped++; + break; + } + rxcb = ATH12K_SKB_RXCB(skb); + + cookie = u32_encode_bits(mac_id, DP_RXDMA_BUF_COOKIE_PDEV_ID) | + u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID); + + ath12k_hal_rx_buf_addr_info_set(rx_mon_status_desc, rxcb->paddr, + cookie, + ab->hw_params->hal_params->rx_buf_rbm); + ath12k_hal_srng_src_get_next_entry(ab, srng); + num_buffs_reaped++; + } + ath12k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return num_buffs_reaped; +} + +static u32 +ath12k_dp_rx_mon_mpdu_pop(struct ath12k *ar, int mac_id, + void *ring_entry, struct sk_buff **head_msdu, + struct sk_buff **tail_msdu, + struct list_head *used_list, + u32 *npackets, u32 *ppdu_id) +{ + struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data; + struct ath12k_buffer_addr *p_buf_addr_info, *p_last_buf_addr_info; + u32 msdu_ppdu_id = 0, msdu_cnt = 0, total_len = 0, frag_len = 0; + u32 rx_buf_size, rx_pkt_offset, sw_cookie; + bool is_frag, is_first_msdu, drop_mpdu = false; + struct hal_reo_entrance_ring *ent_desc = + (struct hal_reo_entrance_ring *)ring_entry; + u32 rx_bufs_used = 0, i = 0, desc_bank = 0; + struct hal_rx_desc *rx_desc, *tail_rx_desc; + struct hal_rx_msdu_link *msdu_link_desc; + struct sk_buff *msdu = NULL, *last = NULL; + struct ath12k_rx_desc_info *desc_info; + struct ath12k_buffer_addr buf_info; + struct hal_rx_msdu_list msdu_list; + struct ath12k_skb_rxcb *rxcb; + u16 num_msdus = 0; + dma_addr_t paddr; + u8 rbm; + + ath12k_hal_rx_reo_ent_buf_paddr_get(ring_entry, &paddr, + &sw_cookie, + &p_last_buf_addr_info, &rbm, + &msdu_cnt); + + spin_lock_bh(&pmon->mon_lock); + + if (le32_get_bits(ent_desc->info1, + HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON) == + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + u8 rxdma_err = le32_get_bits(ent_desc->info1, + HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE); + if (rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR) { + drop_mpdu = true; + pmon->rx_mon_stats.dest_mpdu_drop++; + } + } + + is_frag = false; + is_first_msdu = true; + rx_pkt_offset = sizeof(struct hal_rx_desc); + + do { + if (pmon->mon_last_linkdesc_paddr == paddr) { + pmon->rx_mon_stats.dup_mon_linkdesc_cnt++; + spin_unlock_bh(&pmon->mon_lock); + return rx_bufs_used; + } + + desc_bank = u32_get_bits(sw_cookie, DP_LINK_DESC_BANK_MASK); + msdu_link_desc = + ar->ab->dp.link_desc_banks[desc_bank].vaddr + + (paddr - ar->ab->dp.link_desc_banks[desc_bank].paddr); + + ath12k_hal_rx_msdu_list_get(ar, msdu_link_desc, &msdu_list, + &num_msdus); + desc_info = ath12k_dp_get_rx_desc(ar->ab, + msdu_list.sw_cookie[num_msdus - 1]); + tail_rx_desc = (struct hal_rx_desc *)(desc_info->skb)->data; + + for (i = 0; i < num_msdus; i++) { + u32 l2_hdr_offset; + + if (pmon->mon_last_buf_cookie == msdu_list.sw_cookie[i]) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "i %d last_cookie %d is same\n", + i, pmon->mon_last_buf_cookie); + drop_mpdu = true; + pmon->rx_mon_stats.dup_mon_buf_cnt++; + continue; + } + + desc_info = + ath12k_dp_get_rx_desc(ar->ab, msdu_list.sw_cookie[i]); + msdu = desc_info->skb; + + if (!msdu) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "msdu_pop: invalid msdu (%d/%d)\n", + i + 1, num_msdus); + goto next_msdu; + } + rxcb = ATH12K_SKB_RXCB(msdu); + if (rxcb->paddr != msdu_list.paddr[i]) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "i %d paddr %lx != %lx\n", + i, (unsigned long)rxcb->paddr, + (unsigned long)msdu_list.paddr[i]); + drop_mpdu = true; continue; } + if (!rxcb->unmapped) { + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + + skb_tailroom(msdu), + DMA_FROM_DEVICE); + rxcb->unmapped = 1; + } + if (drop_mpdu) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "i %d drop msdu %p *ppdu_id %x\n", + i, msdu, *ppdu_id); + dev_kfree_skb_any(msdu); + msdu = NULL; + goto next_msdu; + } - if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { - arsta = ath12k_sta_to_arsta(peer->sta); - ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta, - ppdu_info); - } else if ((ppdu_info->fc_valid) && - (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { - ath12k_dp_mon_rx_process_ulofdma(ppdu_info); - ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info); + rx_desc = (struct hal_rx_desc *)msdu->data; + l2_hdr_offset = ath12k_dp_rx_h_l3pad(ar->ab, tail_rx_desc); + if (is_first_msdu) { + if (!ath12k_dp_rxdesc_mpdu_valid(ar->ab, rx_desc)) { + drop_mpdu = true; + dev_kfree_skb_any(msdu); + msdu = NULL; + pmon->mon_last_linkdesc_paddr = paddr; + goto next_msdu; + } + msdu_ppdu_id = + ath12k_dp_rxdesc_get_ppduid(ar->ab, rx_desc); + + if (ath12k_dp_mon_comp_ppduid(msdu_ppdu_id, + ppdu_id)) { + spin_unlock_bh(&pmon->mon_lock); + return rx_bufs_used; + } + pmon->mon_last_linkdesc_paddr = paddr; + is_first_msdu = false; + } + ath12k_dp_mon_get_buf_len(&msdu_list.msdu_info[i], + &is_frag, &total_len, + &frag_len, &msdu_cnt); + rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len; + + if (ath12k_dp_pkt_set_pktlen(msdu, rx_buf_size)) { + dev_kfree_skb_any(msdu); + goto next_msdu; } - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - dev_kfree_skb_any(skb); - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; + if (!(*head_msdu)) + *head_msdu = msdu; + else if (last) + last->next = msdu; + + last = msdu; +next_msdu: + pmon->mon_last_buf_cookie = msdu_list.sw_cookie[i]; + rx_bufs_used++; + desc_info->skb = NULL; + list_add_tail(&desc_info->list, used_list); } - dest_idx = 0; -move_next: - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - ath12k_hal_srng_src_get_next_entry(ab, srng); - num_buffs_reaped++; + ath12k_hal_rx_buf_addr_info_set(&buf_info, paddr, sw_cookie, rbm); + + ath12k_dp_mon_next_link_desc_get(msdu_link_desc, &paddr, + &sw_cookie, &rbm, + &p_buf_addr_info); + + ath12k_dp_rx_link_desc_return(ar->ab, &buf_info, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + + p_last_buf_addr_info = p_buf_addr_info; + + } while (paddr && msdu_cnt); + + spin_unlock_bh(&pmon->mon_lock); + + if (last) + last->next = NULL; + + *tail_msdu = msdu; + + if (msdu_cnt == 0) + *npackets = 1; + + return rx_bufs_used; +} + +/* The destination ring processing is stuck if the destination is not + * moving while status ring moves 16 PPDU. The destination ring processing + * skips this destination ring PPDU as a workaround. + */ +#define MON_DEST_RING_STUCK_MAX_CNT 16 + +static void ath12k_dp_rx_mon_dest_process(struct ath12k *ar, int mac_id, + u32 quota, struct napi_struct *napi) +{ + struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data; + struct ath12k_pdev_mon_stats *rx_mon_stats; + u32 ppdu_id, rx_bufs_used = 0, ring_id; + u32 mpdu_rx_bufs_used, npackets = 0; + struct ath12k_dp *dp = &ar->ab->dp; + struct ath12k_base *ab = ar->ab; + void *ring_entry, *mon_dst_srng; + struct dp_mon_mpdu *tmp_mpdu; + LIST_HEAD(rx_desc_used_list); + struct hal_srng *srng; + + ring_id = dp->rxdma_err_dst_ring[mac_id].ring_id; + srng = &ab->hal.srng_list[ring_id]; + + mon_dst_srng = &ab->hal.srng_list[ring_id]; + + spin_lock_bh(&srng->lock); + + ath12k_hal_srng_access_begin(ab, mon_dst_srng); + + ppdu_id = pmon->mon_ppdu_info.ppdu_id; + rx_mon_stats = &pmon->rx_mon_stats; + + while ((ring_entry = ath12k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { + struct sk_buff *head_msdu, *tail_msdu; + + head_msdu = NULL; + tail_msdu = NULL; + + mpdu_rx_bufs_used = ath12k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry, + &head_msdu, &tail_msdu, + &rx_desc_used_list, + &npackets, &ppdu_id); + + rx_bufs_used += mpdu_rx_bufs_used; + + if (mpdu_rx_bufs_used) { + dp->mon_dest_ring_stuck_cnt = 0; + } else { + dp->mon_dest_ring_stuck_cnt++; + rx_mon_stats->dest_mon_not_reaped++; + } + + if (dp->mon_dest_ring_stuck_cnt > MON_DEST_RING_STUCK_MAX_CNT) { + rx_mon_stats->dest_mon_stuck++; + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "status ring ppdu_id=%d dest ring ppdu_id=%d mon_dest_ring_stuck_cnt=%d dest_mon_not_reaped=%u dest_mon_stuck=%u\n", + pmon->mon_ppdu_info.ppdu_id, ppdu_id, + dp->mon_dest_ring_stuck_cnt, + rx_mon_stats->dest_mon_not_reaped, + rx_mon_stats->dest_mon_stuck); + spin_lock_bh(&pmon->mon_lock); + pmon->mon_ppdu_info.ppdu_id = ppdu_id; + spin_unlock_bh(&pmon->mon_lock); + continue; + } + + if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) { + spin_lock_bh(&pmon->mon_lock); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + spin_unlock_bh(&pmon->mon_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "dest_rx: new ppdu_id %x != status ppdu_id %x dest_mon_not_reaped = %u dest_mon_stuck = %u\n", + ppdu_id, pmon->mon_ppdu_info.ppdu_id, + rx_mon_stats->dest_mon_not_reaped, + rx_mon_stats->dest_mon_stuck); + break; + } + + if (head_msdu && tail_msdu) { + tmp_mpdu = kzalloc(sizeof(*tmp_mpdu), GFP_ATOMIC); + if (!tmp_mpdu) + break; + + tmp_mpdu->head = head_msdu; + tmp_mpdu->tail = tail_msdu; + tmp_mpdu->err_bitmap = pmon->err_bitmap; + tmp_mpdu->decap_format = pmon->decap_format; + ath12k_dp_mon_rx_deliver(ar, tmp_mpdu, + &pmon->mon_ppdu_info, napi); + rx_mon_stats->dest_mpdu_done++; + kfree(tmp_mpdu); + } + + ring_entry = ath12k_hal_srng_dst_get_next_entry(ar->ab, + mon_dst_srng); } + ath12k_hal_srng_access_end(ar->ab, mon_dst_srng); - ath12k_hal_srng_access_end(ab, srng); spin_unlock_bh(&srng->lock); + + if (rx_bufs_used) { + rx_mon_stats->dest_ppdu_done++; + ath12k_dp_rx_bufs_replenish(ar->ab, + &dp->rx_refill_buf_ring, + &rx_desc_used_list, + rx_bufs_used); + } +} + +static int +__ath12k_dp_mon_process_ring(struct ath12k *ar, int mac_id, + struct napi_struct *napi, int *budget) +{ + struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data; + struct ath12k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; + enum hal_rx_mon_status hal_status; + struct sk_buff_head skb_list; + int num_buffs_reaped; + struct sk_buff *skb; + + __skb_queue_head_init(&skb_list); + + num_buffs_reaped = ath12k_dp_rx_reap_mon_status_ring(ar->ab, mac_id, + budget, &skb_list); + if (!num_buffs_reaped) + goto exit; + + while ((skb = __skb_dequeue(&skb_list))) { + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; + + hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); + + if (ar->monitor_started && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && + hal_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath12k_dp_rx_mon_dest_process(ar, mac_id, *budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + + dev_kfree_skb_any(skb); + } + +exit: return num_buffs_reaped; } @@ -2587,11 +4378,14 @@ int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, struct ath12k *ar = ath12k_ab_to_ar(ab, mac_id); int num_buffs_reaped = 0; - if (!ar->monitor_started) - ath12k_dp_mon_rx_process_stats(ar, mac_id, napi, &budget); - else - num_buffs_reaped = ath12k_dp_mon_srng_process(ar, mac_id, &budget, - monitor_mode, napi); + if (ab->hw_params->rxdma1_enable) { + if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) + num_buffs_reaped = ath12k_dp_mon_srng_process(ar, &budget, napi); + } else { + if (ar->monitor_started) + num_buffs_reaped = + __ath12k_dp_mon_process_ring(ar, mac_id, napi, &budget); + } return num_buffs_reaped; } diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h index fb9e9c176ce5..e25595cbdcf3 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.h +++ b/drivers/net/wireless/ath/ath12k/dp_mon.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_MON_H @@ -9,6 +9,9 @@ #include "core.h" +#define ATH12K_MON_RX_DOT11_OFFSET 5 +#define ATH12K_MON_RX_PKT_OFFSET 8 + enum dp_monitor_mode { ATH12K_DP_TX_MONITOR_MODE, ATH12K_DP_RX_MONITOR_MODE @@ -77,14 +80,14 @@ struct dp_mon_tx_ppdu_info { enum hal_rx_mon_status ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, + struct sk_buff *skb, struct napi_struct *napi); int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab, struct dp_rxdma_mon_ring *buf_ring, int req_entries); -int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, - int *budget, enum dp_monitor_mode monitor_mode, - struct napi_struct *napi); +int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab, + struct dp_rxdma_mon_ring *rx_ring, + int req_entries); int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, struct napi_struct *napi, int budget, enum dp_monitor_mode monitor_mode); @@ -96,11 +99,9 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag, enum hal_rx_mon_status ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi, u32 ppdu_id); void ath12k_dp_mon_rx_process_ulofdma(struct hal_rx_mon_ppdu_info *ppdu_info); -int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, - struct napi_struct *napi, int *budget); +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct napi_struct *napi); #endif diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index ca76c018dd0c..57648febc4a4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/ieee80211.h> @@ -17,6 +17,7 @@ #include "dp_tx.h" #include "peer.h" #include "dp_mon.h" +#include "debugfs_htt_stats.h" #define ATH12K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) @@ -193,6 +194,22 @@ static void ath12k_dp_rxdesc_set_msdu_len(struct ath12k_base *ab, ab->hal_rx_ops->rx_desc_set_msdu_len(desc, len); } +u32 ath12k_dp_rxdesc_get_ppduid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc) +{ + return ab->hal_rx_ops->rx_desc_get_mpdu_ppdu_id(rx_desc); +} + +bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc) +{ + u32 tlv_tag; + + tlv_tag = ab->hal_rx_ops->rx_desc_get_mpdu_start_tag(rx_desc); + + return tlv_tag == HAL_RX_MPDU_START; +} + static bool ath12k_dp_rx_h_is_da_mcbc(struct ath12k_base *ab, struct hal_rx_desc *desc) { @@ -227,43 +244,67 @@ static void ath12k_dp_rx_desc_get_crypto_header(struct ath12k_base *ab, ab->hal_rx_ops->rx_desc_get_crypto_header(desc, crypto_hdr, enctype); } -static u16 ath12k_dp_rxdesc_get_mpdu_frame_ctrl(struct ath12k_base *ab, +static inline u8 ath12k_dp_rx_get_msdu_src_link(struct ath12k_base *ab, struct hal_rx_desc *desc) { - return ab->hal_rx_ops->rx_desc_get_mpdu_frame_ctl(desc); + return ab->hal_rx_ops->rx_desc_get_msdu_src_link_id(desc); } -static inline u8 ath12k_dp_rx_get_msdu_src_link(struct ath12k_base *ab, - struct hal_rx_desc *desc) +static void ath12k_dp_clean_up_skb_list(struct sk_buff_head *skb_list) { - return ab->hal_rx_ops->rx_desc_get_msdu_src_link_id(desc); + struct sk_buff *skb; + + while ((skb = __skb_dequeue(skb_list))) + dev_kfree_skb_any(skb); } -static int ath12k_dp_purge_mon_ring(struct ath12k_base *ab) +static size_t ath12k_dp_list_cut_nodes(struct list_head *list, + struct list_head *head, + size_t count) { - int i, reaped = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(DP_MON_PURGE_TIMEOUT_MS); + struct list_head *cur; + struct ath12k_rx_desc_info *rx_desc; + size_t nodes = 0; - do { - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) - reaped += ath12k_dp_mon_process_ring(ab, i, NULL, - DP_MON_SERVICE_BUDGET, - ATH12K_DP_RX_MONITOR_MODE); + if (!count) { + INIT_LIST_HEAD(list); + goto out; + } - /* nothing more to reap */ - if (reaped < DP_MON_SERVICE_BUDGET) - return 0; + list_for_each(cur, head) { + if (!count) + break; - } while (time_before(jiffies, timeout)); + rx_desc = list_entry(cur, struct ath12k_rx_desc_info, list); + rx_desc->in_use = true; - ath12k_warn(ab, "dp mon ring purge timeout"); + count--; + nodes++; + } - return -ETIMEDOUT; + list_cut_before(list, head, cur); +out: + return nodes; +} + +static void ath12k_dp_rx_enqueue_free(struct ath12k_dp *dp, + struct list_head *used_list) +{ + struct ath12k_rx_desc_info *rx_desc, *safe; + + /* Reset the use flag */ + list_for_each_entry_safe(rx_desc, safe, used_list, list) + rx_desc->in_use = false; + + spin_lock_bh(&dp->rx_desc_lock); + list_splice_tail(used_list, &dp->rx_desc_free_list); + spin_unlock_bh(&dp->rx_desc_lock); } /* Returns number of Rx buffers replenished */ int ath12k_dp_rx_bufs_replenish(struct ath12k_base *ab, struct dp_rxdma_ring *rx_ring, + struct list_head *used_list, int req_entries) { struct ath12k_buffer_addr *desc; @@ -292,6 +333,19 @@ int ath12k_dp_rx_bufs_replenish(struct ath12k_base *ab, req_entries = min(num_free, req_entries); num_remain = req_entries; + if (!num_remain) + goto out; + + /* Get the descriptor from free list */ + if (list_empty(used_list)) { + spin_lock_bh(&dp->rx_desc_lock); + req_entries = ath12k_dp_list_cut_nodes(used_list, + &dp->rx_desc_free_list, + num_remain); + spin_unlock_bh(&dp->rx_desc_lock); + num_remain = req_entries; + } + while (num_remain > 0) { skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + DP_RX_BUFFER_ALIGN_SIZE); @@ -311,33 +365,20 @@ int ath12k_dp_rx_bufs_replenish(struct ath12k_base *ab, if (dma_mapping_error(ab->dev, paddr)) goto fail_free_skb; - spin_lock_bh(&dp->rx_desc_lock); - - /* Get desc from free list and store in used list - * for cleanup purposes - * - * TODO: pass the removed descs rather than - * add/read to optimize - */ - rx_desc = list_first_entry_or_null(&dp->rx_desc_free_list, + rx_desc = list_first_entry_or_null(used_list, struct ath12k_rx_desc_info, list); - if (!rx_desc) { - spin_unlock_bh(&dp->rx_desc_lock); + if (!rx_desc) goto fail_dma_unmap; - } rx_desc->skb = skb; cookie = rx_desc->cookie; - list_del(&rx_desc->list); - list_add_tail(&rx_desc->list, &dp->rx_desc_used_list); - - spin_unlock_bh(&dp->rx_desc_lock); desc = ath12k_hal_srng_src_get_next_entry(ab, srng); if (!desc) - goto fail_buf_unassign; + goto fail_dma_unmap; + list_del(&rx_desc->list); ATH12K_SKB_RXCB(skb)->paddr = paddr; num_remain--; @@ -345,26 +386,19 @@ int ath12k_dp_rx_bufs_replenish(struct ath12k_base *ab, ath12k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); } - ath12k_hal_srng_access_end(ab, srng); - - spin_unlock_bh(&srng->lock); + goto out; - return req_entries - num_remain; - -fail_buf_unassign: - spin_lock_bh(&dp->rx_desc_lock); - list_del(&rx_desc->list); - list_add_tail(&rx_desc->list, &dp->rx_desc_free_list); - rx_desc->skb = NULL; - spin_unlock_bh(&dp->rx_desc_lock); fail_dma_unmap: dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); fail_free_skb: dev_kfree_skb_any(skb); - +out: ath12k_hal_srng_access_end(ab, srng); + if (!list_empty(used_list)) + ath12k_dp_rx_enqueue_free(dp, used_list); + spin_unlock_bh(&srng->lock); return req_entries - num_remain; @@ -396,10 +430,16 @@ static int ath12k_dp_rxdma_mon_buf_ring_free(struct ath12k_base *ab, static int ath12k_dp_rxdma_buf_free(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + int i; ath12k_dp_rxdma_mon_buf_ring_free(ab, &dp->rxdma_mon_buf_ring); - ath12k_dp_rxdma_mon_buf_ring_free(ab, &dp->tx_mon_buf_ring); + if (ab->hw_params->rxdma1_enable) + return 0; + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) + ath12k_dp_rxdma_mon_buf_ring_free(ab, + &dp->rx_mon_status_refill_ring[i]); return 0; } @@ -414,7 +454,12 @@ static int ath12k_dp_rxdma_mon_ring_buf_setup(struct ath12k_base *ab, ath12k_hal_srng_get_entrysize(ab, ringtype); rx_ring->bufs_max = num_entries; - ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries); + + if (ringtype == HAL_RXDMA_MONITOR_STATUS) + ath12k_dp_mon_status_bufs_replenish(ab, rx_ring, + num_entries); + else + ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries); return 0; } @@ -422,13 +467,12 @@ static int ath12k_dp_rxdma_mon_ring_buf_setup(struct ath12k_base *ab, static int ath12k_dp_rxdma_ring_buf_setup(struct ath12k_base *ab, struct dp_rxdma_ring *rx_ring) { - int num_entries; + LIST_HEAD(list); - num_entries = rx_ring->refill_buf_ring.size / - ath12k_hal_srng_get_entrysize(ab, HAL_RXDMA_BUF); + rx_ring->bufs_max = rx_ring->refill_buf_ring.size / + ath12k_hal_srng_get_entrysize(ab, HAL_RXDMA_BUF); - rx_ring->bufs_max = num_entries; - ath12k_dp_rx_bufs_replenish(ab, rx_ring, num_entries); + ath12k_dp_rx_bufs_replenish(ab, rx_ring, &list, 0); return 0; } @@ -436,7 +480,8 @@ static int ath12k_dp_rxdma_ring_buf_setup(struct ath12k_base *ab, static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; - int ret; + struct dp_rxdma_mon_ring *mon_ring; + int ret, i; ret = ath12k_dp_rxdma_ring_buf_setup(ab, &dp->rx_refill_buf_ring); if (ret) { @@ -449,18 +494,19 @@ static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab) ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, &dp->rxdma_mon_buf_ring, HAL_RXDMA_MONITOR_BUF); - if (ret) { + if (ret) ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n"); - return ret; - } + return ret; + } - ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, - &dp->tx_mon_buf_ring, - HAL_TX_MONITOR_BUF); + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + mon_ring = &dp->rx_mon_status_refill_ring[i]; + ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, mon_ring, + HAL_RXDMA_MONITOR_STATUS); if (ret) { ath12k_warn(ab, - "failed to setup HAL_TX_MONITOR_BUF\n"); + "failed to setup HAL_RXDMA_MONITOR_STATUS\n"); return ret; } } @@ -474,10 +520,8 @@ static void ath12k_dp_rx_pdev_srng_free(struct ath12k *ar) struct ath12k_base *ab = ar->ab; int i; - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) ath12k_dp_srng_cleanup(ab, &dp->rxdma_mon_dst_ring[i]); - ath12k_dp_srng_cleanup(ab, &dp->tx_mon_dst_ring[i]); - } } void ath12k_dp_rx_pdev_reo_cleanup(struct ath12k_base *ab) @@ -521,7 +565,7 @@ static int ath12k_dp_rx_pdev_srng_alloc(struct ath12k *ar) int ret; u32 mac_id = dp->mac_id; - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ret = ath12k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring[i], HAL_RXDMA_MONITOR_DST, @@ -532,17 +576,6 @@ static int ath12k_dp_rx_pdev_srng_alloc(struct ath12k *ar) "failed to setup HAL_RXDMA_MONITOR_DST\n"); return ret; } - - ret = ath12k_dp_srng_setup(ar->ab, - &dp->tx_mon_dst_ring[i], - HAL_TX_MONITOR_DST, - 0, mac_id + i, - DP_TX_MONITOR_DEST_RING_SIZE); - if (ret) { - ath12k_warn(ar->ab, - "failed to setup HAL_TX_MONITOR_DST\n"); - return ret; - } } return 0; @@ -557,9 +590,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab) spin_lock_bh(&dp->reo_cmd_lock); list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); - dma_unmap_single(ab->dev, cmd->data.paddr, - cmd->data.size, DMA_BIDIRECTIONAL); - kfree(cmd->data.vaddr); + dma_unmap_single(ab->dev, cmd->data.qbuf.paddr_aligned, + cmd->data.qbuf.size, DMA_BIDIRECTIONAL); + kfree(cmd->data.qbuf.vaddr); kfree(cmd); } @@ -567,9 +600,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab) &dp->reo_cmd_cache_flush_list, list) { list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; - dma_unmap_single(ab->dev, cmd_cache->data.paddr, - cmd_cache->data.size, DMA_BIDIRECTIONAL); - kfree(cmd_cache->data.vaddr); + dma_unmap_single(ab->dev, cmd_cache->data.qbuf.paddr_aligned, + cmd_cache->data.qbuf.size, DMA_BIDIRECTIONAL); + kfree(cmd_cache->data.qbuf.vaddr); kfree(cmd_cache); } spin_unlock_bh(&dp->reo_cmd_lock); @@ -584,10 +617,10 @@ static void ath12k_dp_reo_cmd_free(struct ath12k_dp *dp, void *ctx, ath12k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(dp->ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } static int ath12k_dp_reo_cmd_send(struct ath12k_base *ab, struct ath12k_dp_rx_tid *rx_tid, @@ -642,13 +675,13 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, unsigned long tot_desc_sz, desc_sz; int ret; - tot_desc_sz = rx_tid->size; + tot_desc_sz = rx_tid->qbuf.size; desc_sz = ath12k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); while (tot_desc_sz > desc_sz) { tot_desc_sz -= desc_sz; - cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned + tot_desc_sz); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_FLUSH_CACHE, &cmd, NULL); @@ -659,8 +692,8 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, } memset(&cmd, 0, sizeof(cmd)); - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_FLUSH_CACHE, @@ -668,10 +701,10 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, if (ret) { ath12k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", rx_tid->tid, ret); - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } } @@ -730,10 +763,10 @@ static void ath12k_dp_rx_tid_del_func(struct ath12k_dp *dp, void *ctx, return; free_desc: - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u16 tid, @@ -741,36 +774,51 @@ static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u { struct ath12k_reo_queue_ref *qref; struct ath12k_dp *dp = &ab->dp; + bool ml_peer = false; if (!ab->hw_params->reoq_lut_support) return; - /* TODO: based on ML peer or not, select the LUT. below assumes non - * ML peer - */ - qref = (struct ath12k_reo_queue_ref *)dp->reoq_lut.vaddr + - (peer_id * (IEEE80211_NUM_TIDS + 1) + tid); + if (peer_id & ATH12K_PEER_ML_ID_VALID) { + peer_id &= ~ATH12K_PEER_ML_ID_VALID; + ml_peer = true; + } + + if (ml_peer) + qref = (struct ath12k_reo_queue_ref *)dp->ml_reoq_lut.vaddr + + (peer_id * (IEEE80211_NUM_TIDS + 1) + tid); + else + qref = (struct ath12k_reo_queue_ref *)dp->reoq_lut.vaddr + + (peer_id * (IEEE80211_NUM_TIDS + 1) + tid); qref->info0 = u32_encode_bits(lower_32_bits(paddr), BUFFER_ADDR_INFO0_ADDR); qref->info1 = u32_encode_bits(upper_32_bits(paddr), BUFFER_ADDR_INFO1_ADDR) | u32_encode_bits(tid, DP_REO_QREF_NUM); + ath12k_hal_reo_shared_qaddr_cache_clear(ab); } static void ath12k_peer_rx_tid_qref_reset(struct ath12k_base *ab, u16 peer_id, u16 tid) { struct ath12k_reo_queue_ref *qref; struct ath12k_dp *dp = &ab->dp; + bool ml_peer = false; if (!ab->hw_params->reoq_lut_support) return; - /* TODO: based on ML peer or not, select the LUT. below assumes non - * ML peer - */ - qref = (struct ath12k_reo_queue_ref *)dp->reoq_lut.vaddr + - (peer_id * (IEEE80211_NUM_TIDS + 1) + tid); + if (peer_id & ATH12K_PEER_ML_ID_VALID) { + peer_id &= ~ATH12K_PEER_ML_ID_VALID; + ml_peer = true; + } + + if (ml_peer) + qref = (struct ath12k_reo_queue_ref *)dp->ml_reoq_lut.vaddr + + (peer_id * (IEEE80211_NUM_TIDS + 1) + tid); + else + qref = (struct ath12k_reo_queue_ref *)dp->reoq_lut.vaddr + + (peer_id * (IEEE80211_NUM_TIDS + 1) + tid); qref->info0 = u32_encode_bits(0, BUFFER_ADDR_INFO0_ADDR); qref->info1 = u32_encode_bits(0, BUFFER_ADDR_INFO1_ADDR) | @@ -788,8 +836,8 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, return; cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.upd0 = HAL_REO_CMD_UPD0_VLD; ret = ath12k_dp_reo_cmd_send(ar->ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, @@ -797,26 +845,24 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, if (ret) { ath12k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", tid, ret); - dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_unmap_single(ar->ab->dev, rx_tid->qbuf.paddr_aligned, + rx_tid->qbuf.size, DMA_BIDIRECTIONAL); + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } - ath12k_peer_rx_tid_qref_reset(ar->ab, peer->peer_id, tid); + if (peer->mlo) + ath12k_peer_rx_tid_qref_reset(ar->ab, peer->ml_id, tid); + else + ath12k_peer_rx_tid_qref_reset(ar->ab, peer->peer_id, tid); rx_tid->active = false; } -/* TODO: it's strange (and ugly) that struct hal_reo_dest_ring is converted - * to struct hal_wbm_release_ring, I couldn't figure out the logic behind - * that. - */ -static int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, - struct hal_reo_dest_ring *ring, - enum hal_wbm_rel_bm_act action) +int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, + struct ath12k_buffer_addr *buf_addr_info, + enum hal_wbm_rel_bm_act action) { - struct hal_wbm_release_ring *link_desc = (struct hal_wbm_release_ring *)ring; struct hal_wbm_release_ring *desc; struct ath12k_dp *dp = &ab->dp; struct hal_srng *srng; @@ -834,7 +880,7 @@ static int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, goto exit; } - ath12k_hal_rx_msdu_link_desc_set(ab, desc, link_desc, action); + ath12k_hal_rx_msdu_link_desc_set(ab, desc, buf_addr_info, action); exit: ath12k_hal_srng_access_end(ab, srng); @@ -847,14 +893,17 @@ exit: static void ath12k_dp_rx_frags_cleanup(struct ath12k_dp_rx_tid *rx_tid, bool rel_link_desc) { + struct ath12k_buffer_addr *buf_addr_info; struct ath12k_base *ab = rx_tid->ab; lockdep_assert_held(&ab->base_lock); if (rx_tid->dst_ring_desc) { - if (rel_link_desc) - ath12k_dp_rx_link_desc_return(ab, rx_tid->dst_ring_desc, + if (rel_link_desc) { + buf_addr_info = &rx_tid->dst_ring_desc->buf_addr_info; + ath12k_dp_rx_link_desc_return(ab, buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } kfree(rx_tid->dst_ring_desc); rx_tid->dst_ring_desc = NULL; } @@ -879,7 +928,7 @@ void ath12k_dp_rx_peer_tid_cleanup(struct ath12k *ar, struct ath12k_peer *peer) ath12k_dp_rx_frags_cleanup(rx_tid, true); spin_unlock_bh(&ar->ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ar->ab->base_lock); } } @@ -893,8 +942,8 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar, struct ath12k_hal_reo_cmd cmd = {0}; int ret; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; cmd.ba_window_size = ba_win_sz; @@ -918,18 +967,67 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar, return 0; } +static int ath12k_dp_rx_assign_reoq(struct ath12k_base *ab, + struct ath12k_sta *ahsta, + struct ath12k_dp_rx_tid *rx_tid, + u16 ssn, enum hal_pn_type pn_type) +{ + u32 ba_win_sz = rx_tid->ba_win_sz; + struct ath12k_reoq_buf *buf; + void *vaddr, *vaddr_aligned; + dma_addr_t paddr_aligned; + u8 tid = rx_tid->tid; + u32 hw_desc_sz; + int ret; + + buf = &ahsta->reoq_bufs[tid]; + if (!buf->vaddr) { + /* TODO: Optimize the memory allocation for qos tid based on + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); + if (!vaddr) + return -ENOMEM; + + vaddr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); + + ath12k_hal_reo_qdesc_setup(vaddr_aligned, tid, ba_win_sz, + ssn, pn_type); + + paddr_aligned = dma_map_single(ab->dev, vaddr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); + ret = dma_mapping_error(ab->dev, paddr_aligned); + if (ret) { + kfree(vaddr); + return ret; + } + + buf->vaddr = vaddr; + buf->paddr_aligned = paddr_aligned; + buf->size = hw_desc_sz; + } + + rx_tid->qbuf = *buf; + rx_tid->active = true; + + return 0; +} + int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id, u8 tid, u32 ba_win_sz, u16 ssn, enum hal_pn_type pn_type) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = &ab->dp; - struct hal_rx_reo_queue *addr_aligned; struct ath12k_peer *peer; + struct ath12k_sta *ahsta; struct ath12k_dp_rx_tid *rx_tid; - u32 hw_desc_sz; - void *vaddr; - dma_addr_t paddr; + dma_addr_t paddr_aligned; int ret; spin_lock_bh(&ab->base_lock); @@ -941,7 +1039,14 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ return -ENOENT; } - if (ab->hw_params->reoq_lut_support && !dp->reoq_lut.vaddr) { + if (ab->hw_params->dp_primary_link_only && + !peer->primary_link) { + spin_unlock_bh(&ab->base_lock); + return 0; + } + + if (ab->hw_params->reoq_lut_support && + (!dp->reoq_lut.vaddr || !dp->ml_reoq_lut.vaddr)) { spin_unlock_bh(&ab->base_lock); ath12k_warn(ab, "reo qref table is not setup\n"); return -EINVAL; @@ -955,9 +1060,9 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ } rx_tid = &peer->rx_tid[tid]; + paddr_aligned = rx_tid->qbuf.paddr_aligned; /* Update the tid queue if it is already setup */ if (rx_tid->active) { - paddr = rx_tid->paddr; ret = ath12k_peer_rx_tid_reo_update(ar, peer, rx_tid, ba_win_sz, ssn, true); spin_unlock_bh(&ab->base_lock); @@ -969,8 +1074,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ if (!ab->hw_params->reoq_lut_support) { ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, - ba_win_sz); + paddr_aligned, tid, + 1, ba_win_sz); if (ret) { ath12k_warn(ab, "failed to setup peer rx reorder queuefor tid %d: %d\n", tid, ret); @@ -985,70 +1090,58 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ rx_tid->ba_win_sz = ba_win_sz; - /* TODO: Optimize the memory allocation for qos tid based on - * the actual BA window size in REO tid update path. - */ - if (tid == HAL_DESC_REO_NON_QOS_TID) - hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid); - else - hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); - - vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); - if (!vaddr) { - spin_unlock_bh(&ab->base_lock); - return -ENOMEM; - } - - addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - - ath12k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, - ssn, pn_type); - - paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); - - ret = dma_mapping_error(ab->dev, paddr); + ahsta = ath12k_sta_to_ahsta(peer->sta); + ret = ath12k_dp_rx_assign_reoq(ab, ahsta, rx_tid, ssn, pn_type); if (ret) { spin_unlock_bh(&ab->base_lock); - goto err_mem_free; + ath12k_warn(ab, "failed to assign reoq buf for rx tid %u\n", tid); + return ret; } - rx_tid->vaddr = vaddr; - rx_tid->paddr = paddr; - rx_tid->size = hw_desc_sz; - rx_tid->active = true; - if (ab->hw_params->reoq_lut_support) { /* Update the REO queue LUT at the corresponding peer id * and tid with qaddr. */ - ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, paddr); + if (peer->mlo) + ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid, + paddr_aligned); + else + ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, + paddr_aligned); + spin_unlock_bh(&ab->base_lock); } else { spin_unlock_bh(&ab->base_lock); ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, ba_win_sz); + paddr_aligned, tid, 1, + ba_win_sz); } return ret; - -err_mem_free: - kfree(vaddr); - - return ret; } int ath12k_dp_rx_ampdu_start(struct ath12k *ar, - struct ieee80211_ampdu_params *params) + struct ieee80211_ampdu_params *params, + u8 link_id) { struct ath12k_base *ab = ar->ab; - struct ath12k_sta *arsta = ath12k_sta_to_arsta(params->sta); - int vdev_id = arsta->arvif->vdev_id; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta); + struct ath12k_link_sta *arsta; + int vdev_id; int ret; - ret = ath12k_dp_rx_peer_tid_setup(ar, params->sta->addr, vdev_id, + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + ahsta->link[link_id]); + if (!arsta) + return -ENOLINK; + + vdev_id = arsta->arvif->vdev_id; + + ret = ath12k_dp_rx_peer_tid_setup(ar, arsta->addr, vdev_id, params->tid, params->buf_size, - params->ssn, arsta->pn_type); + params->ssn, arsta->ahsta->pn_type); if (ret) ath12k_warn(ab, "failed to setup rx tid %d\n", ret); @@ -1056,18 +1149,29 @@ int ath12k_dp_rx_ampdu_start(struct ath12k *ar, } int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, - struct ieee80211_ampdu_params *params) + struct ieee80211_ampdu_params *params, + u8 link_id) { struct ath12k_base *ab = ar->ab; struct ath12k_peer *peer; - struct ath12k_sta *arsta = ath12k_sta_to_arsta(params->sta); - int vdev_id = arsta->arvif->vdev_id; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta); + struct ath12k_link_sta *arsta; + int vdev_id; bool active; int ret; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + ahsta->link[link_id]); + if (!arsta) + return -ENOLINK; + + vdev_id = arsta->arvif->vdev_id; + spin_lock_bh(&ab->base_lock); - peer = ath12k_peer_find(ab, vdev_id, params->sta->addr); + peer = ath12k_peer_find(ab, vdev_id, arsta->addr); if (!peer) { spin_unlock_bh(&ab->base_lock); ath12k_warn(ab, "failed to find the peer to stop rx aggregation\n"); @@ -1092,7 +1196,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, return ret; } -int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_vif *arvif, +int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_link_vif *arvif, const u8 *peer_addr, enum set_key_cmd key_cmd, struct ieee80211_key_conf *key) @@ -1148,8 +1252,8 @@ int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_vif *arvif, rx_tid = &peer->rx_tid[tid]; if (!rx_tid->active) continue; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, NULL); @@ -1270,10 +1374,10 @@ static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab, return 0; } -static 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, - const void *ptr, void *data), - void *data) +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, + const void *ptr, void *data), + void *data) { const struct htt_tlv *tlv; const void *begin = ptr; @@ -1314,7 +1418,8 @@ ath12k_update_per_peer_tx_stats(struct ath12k *ar, struct ath12k_base *ab = ar->ab; struct ath12k_peer *peer; struct ieee80211_sta *sta; - struct ath12k_sta *arsta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; struct htt_ppdu_stats_user_rate *user_rate; struct ath12k_per_peer_tx_stats *peer_stats = &ar->peer_tx_stats; struct htt_ppdu_user_stats *usr_stats = &ppdu_stats->user_stats[user]; @@ -1395,7 +1500,8 @@ ath12k_update_per_peer_tx_stats(struct ath12k *ar, } sta = peer->sta; - arsta = ath12k_sta_to_arsta(sta); + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; memset(&arsta->txrate, 0, sizeof(arsta->txrate)); @@ -1647,7 +1753,11 @@ static void ath12k_htt_mlo_offset_event_handler(struct ath12k_base *ab, rcu_read_lock(); ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id); if (!ar) { - ath12k_warn(ab, "invalid pdev id %d on htt mlo offset\n", pdev_id); + /* It is possible that the ar is not yet active (started). + * The above function will only look for the active pdev + * and hence %NULL return is possible. Just silently + * discard this message + */ goto exit; } @@ -1730,8 +1840,12 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16); ath12k_dp_get_mac_addr(le32_to_cpu(resp->peer_map_ev.mac_addr_l32), peer_mac_h16, mac_addr); + ast_hash = le32_get_bits(resp->peer_map_ev.info2, + HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL); + hw_peer_id = le32_get_bits(resp->peer_map_ev.info2, + HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID); ath12k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash, - peer_id); + hw_peer_id); break; case HTT_T2H_MSG_TYPE_PEER_UNMAP: case HTT_T2H_MSG_TYPE_PEER_UNMAP2: @@ -1743,6 +1857,7 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, ath12k_htt_pull_ppdu_stats(ab, skb); break; case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: + ath12k_debugfs_htt_ext_stats_handler(ab, skb); break; case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND: ath12k_htt_mlo_offset_event_handler(ab, skb); @@ -1768,6 +1883,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, struct hal_rx_desc *ldesc; int space_extra, rem_len, buf_len; u32 hal_rx_desc_sz = ar->ab->hal.hal_desc_sz; + bool is_continuation; /* As the msdu is spread across multiple rx buffers, * find the offset to the start of msdu for computing @@ -1816,7 +1932,8 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, rem_len = msdu_len - buf_first_len; while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { rxcb = ATH12K_SKB_RXCB(skb); - if (rxcb->is_continuation) + is_continuation = rxcb->is_continuation; + if (is_continuation) buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz; else buf_len = rem_len; @@ -1834,7 +1951,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, dev_kfree_skb_any(skb); rem_len -= buf_len; - if (!rxcb->is_continuation) + if (!is_continuation) break; } @@ -1859,21 +1976,14 @@ static struct sk_buff *ath12k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_ return NULL; } -static void ath12k_dp_rx_h_csum_offload(struct ath12k *ar, struct sk_buff *msdu) +static void ath12k_dp_rx_h_csum_offload(struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info) { - struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - struct ath12k_base *ab = ar->ab; - bool ip_csum_fail, l4_csum_fail; - - ip_csum_fail = ath12k_dp_rx_h_ip_cksum_fail(ab, rxcb->rx_desc); - l4_csum_fail = ath12k_dp_rx_h_l4_cksum_fail(ab, rxcb->rx_desc); - - msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ? - CHECKSUM_NONE : CHECKSUM_UNNECESSARY; + msdu->ip_summed = (rx_info->ip_csum_fail || rx_info->l4_csum_fail) ? + CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, - enum hal_encrypt_type enctype) +int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, enum hal_encrypt_type enctype) { switch (enctype) { case HAL_ENCRYPT_TYPE_OPEN: @@ -2067,10 +2177,13 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k *ar, struct hal_rx_desc *rx_desc = rxcb->rx_desc; struct ath12k_base *ab = ar->ab; size_t hdr_len, crypto_len; - struct ieee80211_hdr *hdr; - u16 qos_ctl; - __le16 fc; - u8 *crypto_hdr; + struct ieee80211_hdr hdr; + __le16 qos_ctl; + u8 *crypto_hdr, mesh_ctrl; + + ath12k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, &hdr); + hdr_len = ieee80211_hdrlen(hdr.frame_control); + mesh_ctrl = ath12k_dp_rx_h_mesh_ctl_present(ab, rx_desc); if (!(status->flag & RX_FLAG_IV_STRIPPED)) { crypto_len = ath12k_dp_rx_crypto_param_len(ar, enctype); @@ -2078,27 +2191,21 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k *ar, ath12k_dp_rx_desc_get_crypto_header(ab, rx_desc, crypto_hdr, enctype); } - fc = cpu_to_le16(ath12k_dp_rxdesc_get_mpdu_frame_ctrl(ab, rx_desc)); - hdr_len = ieee80211_hdrlen(fc); skb_push(msdu, hdr_len); - hdr = (struct ieee80211_hdr *)msdu->data; - hdr->frame_control = fc; - - /* Get wifi header from rx_desc */ - ath12k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, hdr); + memcpy(msdu->data, &hdr, min(hdr_len, sizeof(hdr))); if (rxcb->is_mcbc) status->flag &= ~RX_FLAG_PN_VALIDATED; /* Add QOS header */ - if (ieee80211_is_data_qos(hdr->frame_control)) { - qos_ctl = rxcb->tid; - if (ath12k_dp_rx_h_mesh_ctl_present(ab, rx_desc)) - qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT; + if (ieee80211_is_data_qos(hdr.frame_control)) { + struct ieee80211_hdr *qos_ptr = (struct ieee80211_hdr *)msdu->data; - /* TODO: Add other QoS ctl fields when required */ - memcpy(msdu->data + (hdr_len - IEEE80211_QOS_CTL_LEN), - &qos_ctl, IEEE80211_QOS_CTL_LEN); + qos_ctl = cpu_to_le16(rxcb->tid & IEEE80211_QOS_CTL_TID_MASK); + if (mesh_ctrl) + qos_ctl |= cpu_to_le16(IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT); + + memcpy(ieee80211_get_qos_ctl(qos_ptr), &qos_ctl, IEEE80211_QOS_CTL_LEN); } } @@ -2174,10 +2281,10 @@ static void ath12k_dp_rx_h_undecap(struct ath12k *ar, struct sk_buff *msdu, } struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu) +ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info) { struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - struct hal_rx_desc *rx_desc = rxcb->rx_desc; struct ath12k_peer *peer = NULL; lockdep_assert_held(&ab->base_lock); @@ -2188,40 +2295,41 @@ ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu) if (peer) return peer; - if (!rx_desc || !(ath12k_dp_rxdesc_mac_addr2_valid(ab, rx_desc))) - return NULL; + if (rx_info->addr2_present) + peer = ath12k_peer_find_by_addr(ab, rx_info->addr2); - peer = ath12k_peer_find_by_addr(ab, - ath12k_dp_rxdesc_get_mpdu_start_addr2(ab, - rx_desc)); return peer; } static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, struct sk_buff *msdu, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) + struct ath12k_dp_rx_info *rx_info) { - bool fill_crypto_hdr; struct ath12k_base *ab = ar->ab; struct ath12k_skb_rxcb *rxcb; enum hal_encrypt_type enctype; bool is_decrypted = false; struct ieee80211_hdr *hdr; struct ath12k_peer *peer; + struct ieee80211_rx_status *rx_status = rx_info->rx_status; u32 err_bitmap; /* PN for multicast packets will be checked in mac80211 */ rxcb = ATH12K_SKB_RXCB(msdu); - fill_crypto_hdr = ath12k_dp_rx_h_is_da_mcbc(ar->ab, rx_desc); - rxcb->is_mcbc = fill_crypto_hdr; + rxcb->is_mcbc = rx_info->is_mcbc; if (rxcb->is_mcbc) - rxcb->peer_id = ath12k_dp_rx_h_peer_id(ar->ab, rx_desc); + rxcb->peer_id = rx_info->peer_id; spin_lock_bh(&ar->ab->base_lock); - peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu); + peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, rx_info); if (peer) { + /* resetting mcbc bit because mcbc packets are unicast + * packets only for AP as STA sends unicast packets. + */ + rxcb->is_mcbc = rxcb->is_mcbc && !peer->ucast_ra_only; + if (rxcb->is_mcbc) enctype = peer->sec_type_grp; else @@ -2250,7 +2358,7 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, if (is_decrypted) { rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; - if (fill_crypto_hdr) + if (rx_info->is_mcbc) rx_status->flag |= RX_FLAG_MIC_STRIPPED | RX_FLAG_ICV_STRIPPED; else @@ -2258,37 +2366,28 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, RX_FLAG_PN_VALIDATED; } - ath12k_dp_rx_h_csum_offload(ar, msdu); + ath12k_dp_rx_h_csum_offload(msdu, rx_info); ath12k_dp_rx_h_undecap(ar, msdu, rx_desc, enctype, rx_status, is_decrypted); - if (!is_decrypted || fill_crypto_hdr) + if (!is_decrypted || rx_info->is_mcbc) return; - if (ath12k_dp_rx_h_decap_type(ar->ab, rx_desc) != - DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) { hdr = (void *)msdu->data; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } } -static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) +static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info) { - struct ath12k_base *ab = ar->ab; struct ieee80211_supported_band *sband; - enum rx_msdu_start_pkt_type pkt_type; - u8 bw; - u8 rate_mcs, nss; - u8 sgi; + struct ieee80211_rx_status *rx_status = rx_info->rx_status; + enum rx_msdu_start_pkt_type pkt_type = rx_info->pkt_type; + u8 bw = rx_info->bw, sgi = rx_info->sgi; + u8 rate_mcs = rx_info->rate_mcs, nss = rx_info->nss; bool is_cck; - pkt_type = ath12k_dp_rx_h_pkt_type(ab, rx_desc); - bw = ath12k_dp_rx_h_rx_bw(ab, rx_desc); - rate_mcs = ath12k_dp_rx_h_rate_mcs(ab, rx_desc); - nss = ath12k_dp_rx_h_nss(ab, rx_desc); - sgi = ath12k_dp_rx_h_sgi(ab, rx_desc); - switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: @@ -2337,13 +2436,55 @@ static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc, rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw); break; + case RX_MSDU_START_PKT_TYPE_11BE: + rx_status->rate_idx = rate_mcs; + + if (rate_mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in EHT mode %d\n", + rate_mcs); + break; + } + + rx_status->encoding = RX_ENC_EHT; + rx_status->nss = nss; + rx_status->eht.gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(sgi); + rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw); + break; + default: + break; } } -void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) +void ath12k_dp_rx_h_fetch_info(struct ath12k_base *ab, struct hal_rx_desc *rx_desc, + struct ath12k_dp_rx_info *rx_info) { - struct ath12k_base *ab = ar->ab; + rx_info->ip_csum_fail = ath12k_dp_rx_h_ip_cksum_fail(ab, rx_desc); + rx_info->l4_csum_fail = ath12k_dp_rx_h_l4_cksum_fail(ab, rx_desc); + rx_info->is_mcbc = ath12k_dp_rx_h_is_da_mcbc(ab, rx_desc); + rx_info->decap_type = ath12k_dp_rx_h_decap_type(ab, rx_desc); + rx_info->pkt_type = ath12k_dp_rx_h_pkt_type(ab, rx_desc); + rx_info->sgi = ath12k_dp_rx_h_sgi(ab, rx_desc); + rx_info->rate_mcs = ath12k_dp_rx_h_rate_mcs(ab, rx_desc); + rx_info->bw = ath12k_dp_rx_h_rx_bw(ab, rx_desc); + rx_info->nss = ath12k_dp_rx_h_nss(ab, rx_desc); + rx_info->tid = ath12k_dp_rx_h_tid(ab, rx_desc); + rx_info->peer_id = ath12k_dp_rx_h_peer_id(ab, rx_desc); + rx_info->phy_meta_data = ath12k_dp_rx_h_freq(ab, rx_desc); + + if (ath12k_dp_rxdesc_mac_addr2_valid(ab, rx_desc)) { + ether_addr_copy(rx_info->addr2, + ath12k_dp_rxdesc_get_mpdu_start_addr2(ab, rx_desc)); + rx_info->addr2_present = true; + } + + ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "rx_desc: ", + rx_desc, sizeof(*rx_desc)); +} + +void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info) +{ + struct ieee80211_rx_status *rx_status = rx_info->rx_status; u8 channel_num; u32 center_freq, meta_data; struct ieee80211_channel *channel; @@ -2357,12 +2498,14 @@ void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; - meta_data = ath12k_dp_rx_h_freq(ab, rx_desc); + meta_data = rx_info->phy_meta_data; channel_num = meta_data; center_freq = meta_data >> 16; - if (center_freq >= 5935 && center_freq <= 7105) { + if (center_freq >= ATH12K_MIN_6GHZ_FREQ && + center_freq <= ATH12K_MAX_6GHZ_FREQ) { rx_status->band = NL80211_BAND_6GHZ; + rx_status->freq = center_freq; } else if (channel_num >= 1 && channel_num <= 14) { rx_status->band = NL80211_BAND_2GHZ; } else if (channel_num >= 36 && channel_num <= 173) { @@ -2376,19 +2519,18 @@ void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, ieee80211_frequency_to_channel(channel->center_freq); } spin_unlock_bh(&ar->data_lock); - ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "rx_desc: ", - rx_desc, sizeof(*rx_desc)); } - rx_status->freq = ieee80211_channel_to_frequency(channel_num, - rx_status->band); + if (rx_status->band != NL80211_BAND_6GHZ) + rx_status->freq = ieee80211_channel_to_frequency(channel_num, + rx_status->band); - ath12k_dp_rx_h_rate(ar, rx_desc, rx_status); + ath12k_dp_rx_h_rate(ar, rx_info); } static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *napi, struct sk_buff *msdu, - struct ieee80211_rx_status *status) + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; static const struct ieee80211_radiotap_he known = { @@ -2401,6 +2543,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap struct ieee80211_sta *pubsta; struct ath12k_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); + struct ieee80211_rx_status *status = rx_info->rx_status; u8 decap = DP_RX_DECAP_TYPE_RAW; bool is_mcbc = rxcb->is_mcbc; bool is_eapol = rxcb->is_eapol; @@ -2413,17 +2556,22 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap } if (!(status->flag & RX_FLAG_ONLY_MONITOR)) - decap = ath12k_dp_rx_h_decap_type(ab, rxcb->rx_desc); + decap = rx_info->decap_type; spin_lock_bh(&ab->base_lock); - peer = ath12k_dp_rx_h_find_peer(ab, msdu); + peer = ath12k_dp_rx_h_find_peer(ab, msdu, rx_info); pubsta = peer ? peer->sta : NULL; + if (pubsta && pubsta->valid_links) { + status->link_valid = 1; + status->link_id = peer->link_id; + } + spin_unlock_bh(&ab->base_lock); ath12k_dbg(ab, ATH12K_DBG_DATA, - "rx skb %pK len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", msdu, msdu->len, peer ? peer->addr : NULL, @@ -2434,6 +2582,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap (status->encoding == RX_ENC_HT) ? "ht" : "", (status->encoding == RX_ENC_VHT) ? "vht" : "", (status->encoding == RX_ENC_HE) ? "he" : "", + (status->encoding == RX_ENC_EHT) ? "eht" : "", (status->bw == RATE_INFO_BW_40) ? "40" : "", (status->bw == RATE_INFO_BW_80) ? "80" : "", (status->bw == RATE_INFO_BW_160) ? "160" : "", @@ -2467,10 +2616,33 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } +static bool ath12k_dp_rx_check_nwifi_hdr_len_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc, + struct sk_buff *msdu) +{ + struct ieee80211_hdr *hdr; + u8 decap_type; + u32 hdr_len; + + decap_type = ath12k_dp_rx_h_decap_type(ab, rx_desc); + if (decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI) + return true; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if ((likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN))) + return true; + + ab->device_stats.invalid_rbm++; + WARN_ON_ONCE(1); + return false; +} + static int ath12k_dp_rx_process_msdu(struct ath12k *ar, struct sk_buff *msdu, struct sk_buff_head *msdu_list, - struct ieee80211_rx_status *rx_status) + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; struct hal_rx_desc *rx_desc, *lrx_desc; @@ -2525,10 +2697,16 @@ static int ath12k_dp_rx_process_msdu(struct ath12k *ar, } } - ath12k_dp_rx_h_ppdu(ar, rx_desc, rx_status); - ath12k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) { + ret = -EINVAL; + goto free_out; + } - rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; + ath12k_dp_rx_h_fetch_info(ab, rx_desc, rx_info); + ath12k_dp_rx_h_ppdu(ar, rx_info); + ath12k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_info); + + rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; return 0; @@ -2541,34 +2719,44 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab, struct sk_buff_head *msdu_list, int ring_id) { + struct ath12k_hw_group *ag = ab->ag; struct ieee80211_rx_status rx_status = {0}; struct ath12k_skb_rxcb *rxcb; struct sk_buff *msdu; struct ath12k *ar; - u8 mac_id, pdev_id; + struct ath12k_hw_link *hw_links = ag->hw_links; + struct ath12k_base *partner_ab; + struct ath12k_dp_rx_info rx_info; + u8 hw_link_id, pdev_id; int ret; if (skb_queue_empty(msdu_list)) return; + rx_info.addr2_present = false; + rx_info.rx_status = &rx_status; + rcu_read_lock(); while ((msdu = __skb_dequeue(msdu_list))) { rxcb = ATH12K_SKB_RXCB(msdu); - mac_id = rxcb->mac_id; - pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); - ar = ab->pdevs[pdev_id].ar; - if (!rcu_dereference(ab->pdevs_active[pdev_id])) { + hw_link_id = rxcb->hw_link_id; + partner_ab = ath12k_ag_to_ab(ag, + hw_links[hw_link_id].device_id); + pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params, + hw_links[hw_link_id].pdev_idx); + ar = partner_ab->pdevs[pdev_id].ar; + if (!rcu_dereference(partner_ab->pdevs_active[pdev_id])) { dev_kfree_skb_any(msdu); continue; } - if (test_bit(ATH12K_CAC_RUNNING, &ar->dev_flags)) { + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) { dev_kfree_skb_any(msdu); continue; } - ret = ath12k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status); + ret = ath12k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_info); if (ret) { ath12k_dbg(ab, ATH12K_DBG_DATA, "Unable to process msdu %d", ret); @@ -2576,31 +2764,61 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab, continue; } - ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); + ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_info); } rcu_read_unlock(); } +static u16 ath12k_dp_rx_get_peer_id(struct ath12k_base *ab, + enum ath12k_peer_metadata_version ver, + __le32 peer_metadata) +{ + switch (ver) { + default: + ath12k_warn(ab, "Unknown peer metadata version: %d", ver); + fallthrough; + case ATH12K_PEER_METADATA_V0: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V0_PEER_ID); + case ATH12K_PEER_METADATA_V1: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V1_PEER_ID); + case ATH12K_PEER_METADATA_V1A: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V1A_PEER_ID); + case ATH12K_PEER_METADATA_V1B: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V1B_PEER_ID); + } +} + int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id, struct napi_struct *napi, int budget) { + struct ath12k_hw_group *ag = ab->ag; + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; + struct ath12k_hw_link *hw_links = ag->hw_links; + int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; struct ath12k_rx_desc_info *desc_info; struct ath12k_dp *dp = &ab->dp; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; struct hal_reo_dest_ring *desc; - int num_buffs_reaped = 0; + struct ath12k_base *partner_ab; struct sk_buff_head msdu_list; struct ath12k_skb_rxcb *rxcb; int total_msdu_reaped = 0; + u8 hw_link_id, device_id; struct hal_srng *srng; struct sk_buff *msdu; bool done = false; - int mac_id; u64 desc_va; __skb_queue_head_init(&msdu_list); + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) + INIT_LIST_HEAD(&rx_desc_used_list[device_id]); + srng = &ab->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id]; spin_lock_bh(&srng->lock); @@ -2609,24 +2827,38 @@ try_again: ath12k_hal_srng_access_begin(ab, srng); while ((desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { + struct rx_mpdu_desc *mpdu_info; + struct rx_msdu_desc *msdu_info; enum hal_reo_dest_ring_push_reason push_reason; u32 cookie; cookie = le32_get_bits(desc->buf_addr_info.info1, BUFFER_ADDR_INFO1_SW_COOKIE); - mac_id = le32_get_bits(desc->info0, - HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); + hw_link_id = le32_get_bits(desc->info0, + HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); desc_va = ((u64)le32_to_cpu(desc->buf_va_hi) << 32 | le32_to_cpu(desc->buf_va_lo)); desc_info = (struct ath12k_rx_desc_info *)((unsigned long)desc_va); + device_id = hw_links[hw_link_id].device_id; + partner_ab = ath12k_ag_to_ab(ag, device_id); + if (unlikely(!partner_ab)) { + if (desc_info->skb) { + dev_kfree_skb_any(desc_info->skb); + desc_info->skb = NULL; + } + + continue; + } + /* retry manual desc retrieval */ if (!desc_info) { - desc_info = ath12k_dp_get_rx_desc(ab, cookie); + desc_info = ath12k_dp_get_rx_desc(partner_ab, cookie); if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual desc retrieval"); + ath12k_warn(partner_ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", + cookie); continue; } } @@ -2637,36 +2869,38 @@ try_again: msdu = desc_info->skb; desc_info->skb = NULL; - spin_lock_bh(&dp->rx_desc_lock); - list_move_tail(&desc_info->list, &dp->rx_desc_free_list); - spin_unlock_bh(&dp->rx_desc_lock); + list_add_tail(&desc_info->list, &rx_desc_used_list[device_id]); rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(ab->dev, rxcb->paddr, + dma_unmap_single(partner_ab->dev, rxcb->paddr, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - num_buffs_reaped++; + num_buffs_reaped[device_id]++; + ab->device_stats.reo_rx[ring_id][ab->device_id]++; push_reason = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_PUSH_REASON); if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { dev_kfree_skb_any(msdu); - ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++; + ab->device_stats.hal_reo_error[ring_id]++; continue; } - rxcb->is_first_msdu = !!(le32_to_cpu(desc->rx_msdu_info.info0) & + msdu_info = &desc->rx_msdu_info; + mpdu_info = &desc->rx_mpdu_info; + + rxcb->is_first_msdu = !!(le32_to_cpu(msdu_info->info0) & RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - rxcb->is_last_msdu = !!(le32_to_cpu(desc->rx_msdu_info.info0) & + rxcb->is_last_msdu = !!(le32_to_cpu(msdu_info->info0) & RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - rxcb->is_continuation = !!(le32_to_cpu(desc->rx_msdu_info.info0) & + rxcb->is_continuation = !!(le32_to_cpu(msdu_info->info0) & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); - rxcb->mac_id = mac_id; - rxcb->peer_id = le32_get_bits(desc->rx_mpdu_info.peer_meta_data, - RX_MPDU_DESC_META_DATA_PEER_ID); - rxcb->tid = le32_get_bits(desc->rx_mpdu_info.info0, + rxcb->hw_link_id = hw_link_id; + rxcb->peer_id = ath12k_dp_rx_get_peer_id(ab, dp->peer_metadata_ver, + mpdu_info->peer_meta_data); + rxcb->tid = le32_get_bits(mpdu_info->info0, RX_MPDU_DESC_INFO0_TID); __skb_queue_tail(&msdu_list, msdu); @@ -2700,7 +2934,17 @@ try_again: if (!total_msdu_reaped) goto exit; - ath12k_dp_rx_bufs_replenish(ab, rx_ring, num_buffs_reaped); + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) { + if (!num_buffs_reaped[device_id]) + continue; + + partner_ab = ath12k_ag_to_ab(ag, device_id); + rx_ring = &partner_ab->dp.rx_refill_buf_ring; + + ath12k_dp_rx_bufs_replenish(partner_ab, rx_ring, + &rx_desc_used_list[device_id], + num_buffs_reaped[device_id]); + } ath12k_dp_rx_process_received_packets(ab, napi, &msdu_list, ring_id); @@ -2711,7 +2955,8 @@ exit: static void ath12k_dp_rx_frag_timer(struct timer_list *timer) { - struct ath12k_dp_rx_tid *rx_tid = from_timer(rx_tid, timer, frag_timer); + struct ath12k_dp_rx_tid *rx_tid = timer_container_of(rx_tid, timer, + frag_timer); spin_lock_bh(&rx_tid->ab->base_lock); if (rx_tid->last_frag_no && @@ -2740,10 +2985,17 @@ int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev peer = ath12k_peer_find(ab, vdev_id, peer_mac); if (!peer) { spin_unlock_bh(&ab->base_lock); + crypto_free_shash(tfm); ath12k_warn(ab, "failed to find the peer to set up fragment info\n"); return -ENOENT; } + if (!peer->primary_link) { + spin_unlock_bh(&ab->base_lock); + crypto_free_shash(tfm); + return 0; + } + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) { rx_tid = &peer->rx_tid[i]; rx_tid->ab = ab; @@ -2807,6 +3059,7 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer struct ieee80211_rx_status *rxs = IEEE80211_SKB_RXCB(msdu); struct ieee80211_key_conf *key_conf; struct ieee80211_hdr *hdr; + struct ath12k_dp_rx_info rx_info; u8 mic[IEEE80211_CCMP_MIC_LEN]; int head_len, tail_len, ret; size_t data_len; @@ -2817,6 +3070,9 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer if (ath12k_dp_rx_h_enctype(ab, rx_desc) != HAL_ENCRYPT_TYPE_TKIP_MIC) return 0; + rx_info.addr2_present = false; + rx_info.rx_status = rxs; + hdr = (struct ieee80211_hdr *)(msdu->data + hal_rx_desc_sz); hdr_len = ieee80211_hdrlen(hdr->frame_control); head_len = hdr_len + hal_rx_desc_sz + IEEE80211_TKIP_IV_LEN; @@ -2843,11 +3099,16 @@ mic_fail: (ATH12K_SKB_RXCB(msdu))->is_first_msdu = true; (ATH12K_SKB_RXCB(msdu))->is_last_msdu = true; + ath12k_dp_rx_h_fetch_info(ab, rx_desc, &rx_info); + rxs->flag |= RX_FLAG_MMIC_ERROR | RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; skb_pull(msdu, hal_rx_desc_sz); - ath12k_dp_rx_h_ppdu(ar, rx_desc, rxs); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) + return -EINVAL; + + ath12k_dp_rx_h_ppdu(ar, &rx_info); ath12k_dp_rx_h_undecap(ar, msdu, rx_desc, HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); ieee80211_rx(ath12k_ar_to_hw(ar), msdu); @@ -2969,9 +3230,10 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, struct hal_srng *srng; dma_addr_t link_paddr, buf_paddr; u32 desc_bank, msdu_info, msdu_ext_info, mpdu_info; - u32 cookie, hal_rx_desc_sz, dest_ring_info0; + u32 cookie, hal_rx_desc_sz, dest_ring_info0, queue_addr_hi; int ret; struct ath12k_rx_desc_info *desc_info; + enum hal_rx_buf_return_buf_manager idle_link_rbm = dp->idle_link_rbm; u8 dst_ind; hal_rx_desc_sz = ab->hal.hal_desc_sz; @@ -3005,7 +3267,7 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, buf_paddr = dma_map_single(ab->dev, defrag_skb->data, defrag_skb->len + skb_tailroom(defrag_skb), - DMA_FROM_DEVICE); + DMA_TO_DEVICE); if (dma_mapping_error(ab->dev, buf_paddr)) return -ENOMEM; @@ -3021,9 +3283,9 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, } desc_info->skb = defrag_skb; + desc_info->in_use = true; list_del(&desc_info->list); - list_add_tail(&desc_info->list, &dp->rx_desc_used_list); spin_unlock_bh(&dp->rx_desc_lock); ATH12K_SKB_RXCB(defrag_skb)->paddr = buf_paddr; @@ -3049,7 +3311,7 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, ath12k_hal_rx_buf_addr_info_set(&reo_ent_ring->buf_addr_info, link_paddr, cookie, - HAL_RX_BUF_RBM_WBM_CHIP0_IDLE_DESC_LIST); + idle_link_rbm); mpdu_info = u32_encode_bits(1, RX_MPDU_DESC_INFO0_MSDU_COUNT) | u32_encode_bits(0, RX_MPDU_DESC_INFO0_FRAG_FLAG) | @@ -3061,13 +3323,18 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, reo_ent_ring->rx_mpdu_info.peer_meta_data = reo_dest_ring->rx_mpdu_info.peer_meta_data; - /* Firmware expects physical address to be filled in queue_addr_lo in - * the MLO scenario and in case of non MLO peer meta data needs to be - * filled. - * TODO: Need to handle for MLO scenario. - */ - reo_ent_ring->queue_addr_lo = reo_dest_ring->rx_mpdu_info.peer_meta_data; - reo_ent_ring->info0 = le32_encode_bits(dst_ind, + if (ab->hw_params->reoq_lut_support) { + reo_ent_ring->queue_addr_lo = reo_dest_ring->rx_mpdu_info.peer_meta_data; + queue_addr_hi = 0; + } else { + reo_ent_ring->queue_addr_lo = + cpu_to_le32(lower_32_bits(rx_tid->qbuf.paddr_aligned)); + queue_addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); + } + + reo_ent_ring->info0 = le32_encode_bits(queue_addr_hi, + HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI) | + le32_encode_bits(dst_ind, HAL_REO_ENTR_RING_INFO0_DEST_IND); reo_ent_ring->info1 = le32_encode_bits(rx_tid->cur_sn, @@ -3085,13 +3352,13 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, err_free_desc: spin_lock_bh(&dp->rx_desc_lock); - list_del(&desc_info->list); - list_add_tail(&desc_info->list, &dp->rx_desc_free_list); + desc_info->in_use = false; desc_info->skb = NULL; + list_add_tail(&desc_info->list, &dp->rx_desc_free_list); spin_unlock_bh(&dp->rx_desc_lock); err_unmap_dma: dma_unmap_single(ab->dev, buf_paddr, defrag_skb->len + skb_tailroom(defrag_skb), - DMA_FROM_DEVICE); + DMA_TO_DEVICE); return ret; } @@ -3260,7 +3527,7 @@ static int ath12k_dp_rx_frag_h_mpdu(struct ath12k *ar, goto out_unlock; } } else { - ath12k_dp_rx_link_desc_return(ab, ring_desc, + ath12k_dp_rx_link_desc_return(ab, &ring_desc->buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } @@ -3272,7 +3539,7 @@ static int ath12k_dp_rx_frag_h_mpdu(struct ath12k *ar, } spin_unlock_bh(&ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ab->base_lock); peer = ath12k_peer_find_by_id(ab, peer_id); @@ -3304,6 +3571,7 @@ out_unlock: static int ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, + struct list_head *used_list, bool drop, u32 cookie) { struct ath12k_base *ab = ar->ab; @@ -3323,7 +3591,8 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, if (!desc_info) { desc_info = ath12k_dp_get_rx_desc(ab, cookie); if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual desc retrieval"); + ath12k_warn(ab, "Invalid cookie in DP rx error descriptor retrieval: 0x%x\n", + cookie); return -EINVAL; } } @@ -3333,9 +3602,8 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, msdu = desc_info->skb; desc_info->skb = NULL; - spin_lock_bh(&ab->dp.rx_desc_lock); - list_move_tail(&desc_info->list, &ab->dp.rx_desc_free_list); - spin_unlock_bh(&ab->dp.rx_desc_lock); + + list_add_tail(&desc_info->list, used_list); rxcb = ATH12K_SKB_RXCB(msdu); dma_unmap_single(ar->ab->dev, rxcb->paddr, @@ -3353,7 +3621,7 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, goto exit; } - if (test_bit(ATH12K_CAC_RUNNING, &ar->dev_flags)) { + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) { dev_kfree_skb_any(msdu); goto exit; } @@ -3372,7 +3640,7 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, if (ath12k_dp_rx_frag_h_mpdu(ar, msdu, desc)) { dev_kfree_skb_any(msdu); - ath12k_dp_rx_link_desc_return(ar->ab, desc, + ath12k_dp_rx_link_desc_return(ar->ab, &desc->buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } exit: @@ -3383,7 +3651,10 @@ exit: int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, int budget) { + struct ath12k_hw_group *ag = ab->ag; + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; struct dp_link_desc_bank *link_desc_banks; enum hal_rx_buf_return_buf_manager rbm; struct hal_rx_msdu_link *link_desc_va; @@ -3391,22 +3662,24 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, struct hal_reo_dest_ring *reo_desc; struct dp_rxdma_ring *rx_ring; struct dp_srng *reo_except; + struct ath12k_hw_link *hw_links = ag->hw_links; + struct ath12k_base *partner_ab; + u8 hw_link_id, device_id; u32 desc_bank, num_msdus; struct hal_srng *srng; - struct ath12k_dp *dp; - int mac_id; struct ath12k *ar; dma_addr_t paddr; bool is_frag; - bool drop = false; + bool drop; int pdev_id; tot_n_bufs_reaped = 0; quota = budget; - dp = &ab->dp; - reo_except = &dp->reo_except_ring; - link_desc_banks = dp->link_desc_banks; + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) + INIT_LIST_HEAD(&rx_desc_used_list[device_id]); + + reo_except = &ab->dp.reo_except_ring; srng = &ab->hal.srng_list[reo_except->ring_id]; @@ -3416,7 +3689,9 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, while (budget && (reo_desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { - ab->soc_stats.err_ring_pkts++; + drop = false; + ab->device_stats.err_ring_pkts++; + ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr, &desc_bank); if (ret) { @@ -3424,16 +3699,28 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, ret); continue; } + + hw_link_id = le32_get_bits(reo_desc->info0, + HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); + device_id = hw_links[hw_link_id].device_id; + partner_ab = ath12k_ag_to_ab(ag, device_id); + + pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params, + hw_links[hw_link_id].pdev_idx); + ar = partner_ab->pdevs[pdev_id].ar; + + link_desc_banks = partner_ab->dp.link_desc_banks; link_desc_va = link_desc_banks[desc_bank].vaddr + (paddr - link_desc_banks[desc_bank].paddr); ath12k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, &rbm); - if (rbm != HAL_RX_BUF_RBM_WBM_CHIP0_IDLE_DESC_LIST && + if (rbm != partner_ab->dp.idle_link_rbm && rbm != HAL_RX_BUF_RBM_SW3_BM && - rbm != ab->hw_params->hal_params->rx_buf_rbm) { - ab->soc_stats.invalid_rbm++; + rbm != partner_ab->hw_params->hal_params->rx_buf_rbm) { + ab->device_stats.invalid_rbm++; ath12k_warn(ab, "invalid return buffer manager %d\n", rbm); - ath12k_dp_rx_link_desc_return(ab, reo_desc, + ath12k_dp_rx_link_desc_return(partner_ab, + &reo_desc->buf_addr_info, HAL_WBM_REL_BM_ACT_REL_MSDU); continue; } @@ -3443,24 +3730,27 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, /* Process only rx fragments with one msdu per link desc below, and drop * msdu's indicated due to error reasons. + * Dynamic fragmentation not supported in Multi-link client, so drop the + * partner device buffers. */ - if (!is_frag || num_msdus > 1) { + if (!is_frag || num_msdus > 1 || + partner_ab->device_id != ab->device_id) { drop = true; + /* Return the link desc back to wbm idle list */ - ath12k_dp_rx_link_desc_return(ab, reo_desc, + ath12k_dp_rx_link_desc_return(partner_ab, + &reo_desc->buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } for (i = 0; i < num_msdus; i++) { - mac_id = le32_get_bits(reo_desc->info0, - HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); - - pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); - ar = ab->pdevs[pdev_id].ar; - - if (!ath12k_dp_process_rx_err_buf(ar, reo_desc, drop, - msdu_cookies[i])) + if (!ath12k_dp_process_rx_err_buf(ar, reo_desc, + &rx_desc_used_list[device_id], + drop, + msdu_cookies[i])) { + num_buffs_reaped[device_id]++; tot_n_bufs_reaped++; + } } if (tot_n_bufs_reaped >= quota) { @@ -3476,9 +3766,17 @@ exit: spin_unlock_bh(&srng->lock); - rx_ring = &dp->rx_refill_buf_ring; + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) { + if (!num_buffs_reaped[device_id]) + continue; + + partner_ab = ath12k_ag_to_ab(ag, device_id); + rx_ring = &partner_ab->dp.rx_refill_buf_ring; - ath12k_dp_rx_bufs_replenish(ab, rx_ring, tot_n_bufs_reaped); + ath12k_dp_rx_bufs_replenish(partner_ab, rx_ring, + &rx_desc_used_list[device_id], + num_buffs_reaped[device_id]); + } return tot_n_bufs_reaped; } @@ -3508,7 +3806,7 @@ static void ath12k_dp_rx_null_q_desc_sg_drop(struct ath12k *ar, } static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status, + struct ath12k_dp_rx_info *rx_info, struct sk_buff_head *msdu_list) { struct ath12k_base *ab = ar->ab; @@ -3561,11 +3859,14 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } - ath12k_dp_rx_h_ppdu(ar, desc, status); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return -EINVAL; - ath12k_dp_rx_h_mpdu(ar, msdu, desc, status); + ath12k_dp_rx_h_fetch_info(ab, desc, rx_info); + ath12k_dp_rx_h_ppdu(ar, rx_info); + ath12k_dp_rx_h_mpdu(ar, msdu, desc, rx_info); - rxcb->tid = ath12k_dp_rx_h_tid(ab, desc); + rxcb->tid = rx_info->tid; /* Please note that caller will having the access to msdu and completing * rx with mac80211. Need not worry about cleaning up amsdu_list. @@ -3575,17 +3876,17 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, } static bool ath12k_dp_rx_h_reo_err(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status, + struct ath12k_dp_rx_info *rx_info, struct sk_buff_head *msdu_list) { struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); bool drop = false; - ar->ab->soc_stats.reo_error[rxcb->err_code]++; + ar->ab->device_stats.reo_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO: - if (ath12k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list)) + if (ath12k_dp_rx_h_null_q_desc(ar, msdu, rx_info, msdu_list)) drop = true; break; case HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED: @@ -3605,8 +3906,8 @@ static bool ath12k_dp_rx_h_reo_err(struct ath12k *ar, struct sk_buff *msdu, return drop; } -static void ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status) +static bool ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; u16 msdu_len; @@ -3620,20 +3921,33 @@ static void ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu, l3pad_bytes = ath12k_dp_rx_h_l3pad(ab, desc); msdu_len = ath12k_dp_rx_h_msdu_len(ab, desc); + + if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "invalid msdu len in tkip mic err %u\n", msdu_len); + ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "", desc, + sizeof(*desc)); + return true; + } + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); - ath12k_dp_rx_h_ppdu(ar, desc, status); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return true; + + ath12k_dp_rx_h_ppdu(ar, rx_info); - status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | - RX_FLAG_DECRYPTED); + rx_info->rx_status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED); ath12k_dp_rx_h_undecap(ar, msdu, desc, - HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); + HAL_ENCRYPT_TYPE_TKIP_MIC, rx_info->rx_status, false); + return false; } static bool ath12k_dp_rx_h_rxdma_err(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status) + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); @@ -3641,14 +3955,15 @@ static bool ath12k_dp_rx_h_rxdma_err(struct ath12k *ar, struct sk_buff *msdu, bool drop = false; u32 err_bitmap; - ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; + ar->ab->device_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR: case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: err_bitmap = ath12k_dp_rx_h_mpdu_err(ab, rx_desc); if (err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) { - ath12k_dp_rx_h_tkip_mic_err(ar, msdu, status); + ath12k_dp_rx_h_fetch_info(ab, rx_desc, rx_info); + drop = ath12k_dp_rx_h_tkip_mic_err(ar, msdu, rx_info); break; } fallthrough; @@ -3670,14 +3985,18 @@ static void ath12k_dp_rx_wbm_err(struct ath12k *ar, { struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ieee80211_rx_status rxs = {0}; + struct ath12k_dp_rx_info rx_info; bool drop = true; + rx_info.addr2_present = false; + rx_info.rx_status = &rxs; + switch (rxcb->err_rel_src) { case HAL_WBM_REL_SRC_MODULE_REO: - drop = ath12k_dp_rx_h_reo_err(ar, msdu, &rxs, msdu_list); + drop = ath12k_dp_rx_h_reo_err(ar, msdu, &rx_info, msdu_list); break; case HAL_WBM_REL_SRC_MODULE_RXDMA: - drop = ath12k_dp_rx_h_rxdma_err(ar, msdu, &rxs); + drop = ath12k_dp_rx_h_rxdma_err(ar, msdu, &rx_info); break; default: /* msdu will get freed */ @@ -3689,31 +4008,40 @@ static void ath12k_dp_rx_wbm_err(struct ath12k *ar, return; } - ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rxs); + ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_info); } int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, struct napi_struct *napi, int budget) { + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; + struct ath12k_hw_group *ag = ab->ag; struct ath12k *ar; struct ath12k_dp *dp = &ab->dp; struct dp_rxdma_ring *rx_ring; struct hal_rx_wbm_rel_info err_info; struct hal_srng *srng; struct sk_buff *msdu; - struct sk_buff_head msdu_list; + struct sk_buff_head msdu_list, scatter_msdu_list; struct ath12k_skb_rxcb *rxcb; void *rx_desc; - u8 mac_id; - int num_buffs_reaped = 0; + int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; + int total_num_buffs_reaped = 0; struct ath12k_rx_desc_info *desc_info; + struct ath12k_device_dp_stats *device_stats = &ab->device_stats; + struct ath12k_hw_link *hw_links = ag->hw_links; + struct ath12k_base *partner_ab; + u8 hw_link_id, device_id; int ret, pdev_id; + struct hal_rx_desc *msdu_data; __skb_queue_head_init(&msdu_list); + __skb_queue_head_init(&scatter_msdu_list); - srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; - rx_ring = &dp->rx_refill_buf_ring; + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) + INIT_LIST_HEAD(&rx_desc_used_list[device_id]); + srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; spin_lock_bh(&srng->lock); ath12k_hal_srng_access_begin(ab, srng); @@ -3737,7 +4065,8 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, if (!desc_info) { desc_info = ath12k_dp_get_rx_desc(ab, err_info.cookie); if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual desc retrieval"); + ath12k_warn(ab, "Invalid cookie in DP WBM rx error descriptor retrieval: 0x%x\n", + err_info.cookie); continue; } } @@ -3748,16 +4077,27 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, msdu = desc_info->skb; desc_info->skb = NULL; - spin_lock_bh(&dp->rx_desc_lock); - list_move_tail(&desc_info->list, &dp->rx_desc_free_list); - spin_unlock_bh(&dp->rx_desc_lock); + device_id = desc_info->device_id; + partner_ab = ath12k_ag_to_ab(ag, device_id); + if (unlikely(!partner_ab)) { + dev_kfree_skb_any(msdu); + + /* In any case continuation bit is set + * in the previous record, cleanup scatter_msdu_list + */ + ath12k_dp_clean_up_skb_list(&scatter_msdu_list); + continue; + } + + list_add_tail(&desc_info->list, &rx_desc_used_list[device_id]); rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(ab->dev, rxcb->paddr, + dma_unmap_single(partner_ab->dev, rxcb->paddr, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - num_buffs_reaped++; + num_buffs_reaped[device_id]++; + total_num_buffs_reaped++; if (!err_info.continuation) budget--; @@ -3768,47 +4108,111 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, continue; } + msdu_data = (struct hal_rx_desc *)msdu->data; rxcb->err_rel_src = err_info.err_rel_src; rxcb->err_code = err_info.err_code; - rxcb->rx_desc = (struct hal_rx_desc *)msdu->data; - - __skb_queue_tail(&msdu_list, msdu); - rxcb->is_first_msdu = err_info.first_msdu; rxcb->is_last_msdu = err_info.last_msdu; rxcb->is_continuation = err_info.continuation; + rxcb->rx_desc = msdu_data; + + if (err_info.continuation) { + __skb_queue_tail(&scatter_msdu_list, msdu); + continue; + } + + hw_link_id = ath12k_dp_rx_get_msdu_src_link(partner_ab, + msdu_data); + if (hw_link_id >= ATH12K_GROUP_MAX_RADIO) { + dev_kfree_skb_any(msdu); + + /* In any case continuation bit is set + * in the previous record, cleanup scatter_msdu_list + */ + ath12k_dp_clean_up_skb_list(&scatter_msdu_list); + continue; + } + + if (!skb_queue_empty(&scatter_msdu_list)) { + struct sk_buff *msdu; + + skb_queue_walk(&scatter_msdu_list, msdu) { + rxcb = ATH12K_SKB_RXCB(msdu); + rxcb->hw_link_id = hw_link_id; + } + + skb_queue_splice_tail_init(&scatter_msdu_list, + &msdu_list); + } + + rxcb = ATH12K_SKB_RXCB(msdu); + rxcb->hw_link_id = hw_link_id; + __skb_queue_tail(&msdu_list, msdu); } + /* In any case continuation bit is set in the + * last record, cleanup scatter_msdu_list + */ + ath12k_dp_clean_up_skb_list(&scatter_msdu_list); + ath12k_hal_srng_access_end(ab, srng); spin_unlock_bh(&srng->lock); - if (!num_buffs_reaped) + if (!total_num_buffs_reaped) goto done; - ath12k_dp_rx_bufs_replenish(ab, rx_ring, num_buffs_reaped); + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) { + if (!num_buffs_reaped[device_id]) + continue; + + partner_ab = ath12k_ag_to_ab(ag, device_id); + rx_ring = &partner_ab->dp.rx_refill_buf_ring; + + ath12k_dp_rx_bufs_replenish(ab, rx_ring, + &rx_desc_used_list[device_id], + num_buffs_reaped[device_id]); + } rcu_read_lock(); while ((msdu = __skb_dequeue(&msdu_list))) { - mac_id = ath12k_dp_rx_get_msdu_src_link(ab, - (struct hal_rx_desc *)msdu->data); - pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); - ar = ab->pdevs[pdev_id].ar; + rxcb = ATH12K_SKB_RXCB(msdu); + hw_link_id = rxcb->hw_link_id; + + device_id = hw_links[hw_link_id].device_id; + partner_ab = ath12k_ag_to_ab(ag, device_id); + if (unlikely(!partner_ab)) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "Unable to process WBM error msdu due to invalid hw link id %d device id %d\n", + hw_link_id, device_id); + dev_kfree_skb_any(msdu); + continue; + } - if (!ar || !rcu_dereference(ar->ab->pdevs_active[mac_id])) { + pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params, + hw_links[hw_link_id].pdev_idx); + ar = partner_ab->pdevs[pdev_id].ar; + + if (!ar || !rcu_dereference(ar->ab->pdevs_active[pdev_id])) { dev_kfree_skb_any(msdu); continue; } - if (test_bit(ATH12K_CAC_RUNNING, &ar->dev_flags)) { + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) { dev_kfree_skb_any(msdu); continue; } + + if (rxcb->err_rel_src < HAL_WBM_REL_SRC_MODULE_MAX) { + device_id = ar->ab->device_id; + device_stats->rx_wbm_rel_source[rxcb->err_rel_src][device_id]++; + } + ath12k_dp_rx_wbm_err(ar, napi, msdu, &msdu_list); } rcu_read_unlock(); done: - return num_buffs_reaped; + return total_num_buffs_reaped; } void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab) @@ -3830,7 +4234,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 = u64_get_bits(hdr->tl, HAL_SRNG_TLV_HDR_TAG); + tag = le64_get_bits(hdr->tl, HAL_SRNG_TLV_HDR_TAG); switch (tag) { case HAL_REO_GET_QUEUE_STATS_STATUS: @@ -3893,20 +4297,24 @@ void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab) void ath12k_dp_rx_free(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + struct dp_srng *srng; int i; ath12k_dp_srng_cleanup(ab, &dp->rx_refill_buf_ring.refill_buf_ring); - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { if (ab->hw_params->rx_mac_buf_ring) ath12k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]); + if (!ab->hw_params->rxdma1_enable) { + srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; + ath12k_dp_srng_cleanup(ab, srng); + } } for (i = 0; i < ab->hw_params->num_rxdma_dst_ring; i++) ath12k_dp_srng_cleanup(ab, &dp->rxdma_err_dst_ring[i]); ath12k_dp_srng_cleanup(ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); - ath12k_dp_srng_cleanup(ab, &dp->tx_mon_buf_ring.refill_buf_ring); ath12k_dp_rxdma_buf_free(ab); } @@ -3964,7 +4372,7 @@ int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab) struct ath12k_dp *dp = &ab->dp; struct htt_rx_ring_tlv_filter tlv_filter = {0}; u32 ring_id; - int ret; + int ret = 0; u32 hal_rx_desc_sz = ab->hal.hal_desc_sz; int i; @@ -3990,7 +4398,7 @@ int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab) * and modify the rx_desc struct */ - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + 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, @@ -4017,7 +4425,7 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) } if (ab->hw_params->rx_mac_buf_ring) { - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + 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_srng_setup(ab, ring_id, i, HAL_RXDMA_BUF); @@ -4049,14 +4457,18 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) ret); return ret; } - - ring_id = dp->tx_mon_buf_ring.refill_buf_ring.ring_id; - ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, - 0, HAL_TX_MONITOR_BUF); - if (ret) { - ath12k_warn(ab, "failed to configure rxdma_mon_buf_ring %d\n", - ret); - return ret; + } else { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = + dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, i, + HAL_RXDMA_MONITOR_STATUS); + if (ret) { + ath12k_warn(ab, + "failed to configure mon_status_refill_ring%d %d\n", + i, ret); + return ret; + } } } @@ -4072,14 +4484,12 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) int ath12k_dp_rx_alloc(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + struct dp_srng *srng; int i, ret; idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); - idr_init(&dp->tx_mon_buf_ring.bufs_idr); - spin_lock_init(&dp->tx_mon_buf_ring.idr_lock); - ret = ath12k_dp_srng_setup(ab, &dp->rx_refill_buf_ring.refill_buf_ring, HAL_RXDMA_BUF, 0, 0, @@ -4090,7 +4500,7 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab) } if (ab->hw_params->rx_mac_buf_ring) { - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ret = ath12k_dp_srng_setup(ab, &dp->rx_mac_buf_ring[i], HAL_RXDMA_BUF, 1, @@ -4122,14 +4532,22 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab) ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n"); return ret; } + } else { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + idr_init(&dp->rx_mon_status_refill_ring[i].bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring[i].idr_lock); + } - ret = ath12k_dp_srng_setup(ab, - &dp->tx_mon_buf_ring.refill_buf_ring, - HAL_TX_MONITOR_BUF, 0, 0, - DP_TX_MONITOR_BUF_RING_SIZE); - if (ret) { - ath12k_warn(ab, "failed to setup DP_TX_MONITOR_BUF_RING_SIZE\n"); - return ret; + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; + ret = ath12k_dp_srng_setup(ab, srng, + HAL_RXDMA_MONITOR_STATUS, 0, i, + DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + ath12k_warn(ab, "failed to setup mon status ring %d\n", + i); + return ret; + } } } @@ -4159,7 +4577,7 @@ int ath12k_dp_rx_pdev_alloc(struct ath12k_base *ab, int mac_id) return ret; } - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ring_id = dp->rxdma_mon_dst_ring[i].ring_id; ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, mac_id + i, @@ -4170,17 +4588,6 @@ int ath12k_dp_rx_pdev_alloc(struct ath12k_base *ab, int mac_id) i, ret); return ret; } - - ring_id = dp->tx_mon_dst_ring[i].ring_id; - ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, - mac_id + i, - HAL_TX_MONITOR_DST); - if (ret) { - ath12k_warn(ab, - "failed to configure tx_mon_dst_ring %d %d\n", - i, ret); - return ret; - } } out: return 0; @@ -4212,41 +4619,15 @@ int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar) return ret; } - /* if rxdma1_enable is false, no need to setup - * rxdma_mon_desc_ring. - */ - if (!ar->ab->hw_params->rxdma1_enable) - return 0; - pmon->mon_last_linkdesc_paddr = 0; pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1; spin_lock_init(&pmon->mon_lock); - return 0; -} - -int ath12k_dp_rx_pktlog_start(struct ath12k_base *ab) -{ - /* start reap timer */ - mod_timer(&ab->mon_reap_timer, - jiffies + msecs_to_jiffies(ATH12K_MON_TIMER_INTERVAL)); - - return 0; -} - -int ath12k_dp_rx_pktlog_stop(struct ath12k_base *ab, bool stop_timer) -{ - int ret; - - if (stop_timer) - del_timer_sync(&ab->mon_reap_timer); + if (!ar->ab->hw_params->rxdma1_enable) + return 0; - /* reap all the monitor related rings */ - ret = ath12k_dp_purge_mon_ring(ab); - if (ret) { - ath12k_warn(ab, "failed to purge dp mon ring: %d\n", ret); - return ret; - } + INIT_LIST_HEAD(&pmon->dp_rx_mon_mpdu_list); + pmon->mon_mpdu = NULL; return 0; } diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 05b3d5581dbe..e971a314bd2d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_RX_H #define ATH12K_DP_RX_H @@ -14,11 +14,9 @@ struct ath12k_dp_rx_tid { u8 tid; - u32 *vaddr; - dma_addr_t paddr; - u32 size; u32 ba_win_sz; bool active; + struct ath12k_reoq_buf qbuf; /* Info related to rx fragments */ u32 cur_sn; @@ -65,6 +63,24 @@ struct ath12k_dp_rx_rfc1042_hdr { __be16 snap_type; } __packed; +struct ath12k_dp_rx_info { + struct ieee80211_rx_status *rx_status; + u32 phy_meta_data; + u16 peer_id; + u8 decap_type; + u8 pkt_type; + u8 sgi; + u8 rate_mcs; + u8 bw; + u8 nss; + u8 addr2[ETH_ALEN]; + u8 tid; + bool ip_csum_fail; + bool l4_csum_fail; + bool is_mcbc; + bool addr2_present; +}; + static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi) { u32 ret = 0; @@ -79,16 +95,21 @@ static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi) case RX_MSDU_START_SGI_3_2_US: ret = NL80211_RATE_INFO_HE_GI_3_2; break; + default: + ret = NL80211_RATE_INFO_HE_GI_0_8; + break; } return ret; } int ath12k_dp_rx_ampdu_start(struct ath12k *ar, - struct ieee80211_ampdu_params *params); + struct ieee80211_ampdu_params *params, + u8 link_id); int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, - struct ieee80211_ampdu_params *params); -int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_vif *arvif, + struct ieee80211_ampdu_params *params, + u8 link_id); +int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_link_vif *arvif, const u8 *peer_addr, enum set_key_cmd key_cmd, struct ieee80211_key_conf *key); @@ -118,26 +139,39 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int mac_id, int budget); int ath12k_dp_rx_bufs_replenish(struct ath12k_base *ab, struct dp_rxdma_ring *rx_ring, + struct list_head *used_list, int req_entries); int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar); int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id); -int ath12k_dp_rx_pktlog_start(struct ath12k_base *ab); -int ath12k_dp_rx_pktlog_stop(struct ath12k_base *ab, bool stop_timer); u8 ath12k_dp_rx_h_l3pad(struct ath12k_base *ab, struct hal_rx_desc *desc); struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu); +ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info); u8 ath12k_dp_rx_h_decap_type(struct ath12k_base *ab, struct hal_rx_desc *desc); u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab, struct hal_rx_desc *desc); -void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status); -struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu); - +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_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data); +void ath12k_dp_rx_h_fetch_info(struct ath12k_base *ab, struct hal_rx_desc *rx_desc, + struct ath12k_dp_rx_info *rx_info); + +int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, enum hal_encrypt_type enctype); +u32 ath12k_dp_rxdesc_get_ppduid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc); +bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc); +int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, + struct ath12k_buffer_addr *buf_addr_info, + enum hal_wbm_rel_bm_act action); +bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc); #endif /* ATH12K_DP_RX_H */ diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 572b87153647..b6816b6c2c04 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -1,16 +1,19 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" #include "dp_tx.h" #include "debug.h" +#include "debugfs.h" #include "hw.h" +#include "peer.h" +#include "mac.h" static enum hal_tcl_encap_type -ath12k_dp_tx_get_encap_type(struct ath12k_vif *arvif, struct sk_buff *skb) +ath12k_dp_tx_get_encap_type(struct ath12k_link_vif *arvif, struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath12k_base *ab = arvif->ar->ab; @@ -81,6 +84,7 @@ static void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp, u8 pool_id) { spin_lock_bh(&dp->tx_desc_lock[pool_id]); + tx_desc->skb_ext_desc = NULL; list_move_tail(&tx_desc->list, &dp->tx_desc_free_list[pool_id]); spin_unlock_bh(&dp->tx_desc_lock[pool_id]); } @@ -117,15 +121,108 @@ static void ath12k_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->data_len, HAL_TX_MSDU_EXT_INFO1_BUF_LEN); - tcl_ext_cmd->info1 = le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) | + tcl_ext_cmd->info1 |= le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) | le32_encode_bits(ti->encap_type, HAL_TX_MSDU_EXT_INFO1_ENCAP_TYPE) | le32_encode_bits(ti->encrypt_type, HAL_TX_MSDU_EXT_INFO1_ENCRYPT_TYPE); } -int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif, - struct sk_buff *skb) +#define HTT_META_DATA_ALIGNMENT 0x8 + +static void *ath12k_dp_metadata_align_skb(struct sk_buff *skb, u8 tail_len) +{ + struct sk_buff *tail; + void *metadata; + + if (unlikely(skb_cow_data(skb, tail_len, &tail) < 0)) + return NULL; + + metadata = pskb_put(skb, tail, tail_len); + memset(metadata, 0, tail_len); + return metadata; +} + +/* Preparing HTT Metadata when utilized with ext MSDU */ +static int ath12k_dp_prepare_htt_metadata(struct sk_buff *skb) +{ + struct hal_tx_msdu_metadata *desc_ext; + u8 htt_desc_size; + /* Size rounded of multiple of 8 bytes */ + u8 htt_desc_size_aligned; + + htt_desc_size = sizeof(struct hal_tx_msdu_metadata); + htt_desc_size_aligned = ALIGN(htt_desc_size, HTT_META_DATA_ALIGNMENT); + + desc_ext = ath12k_dp_metadata_align_skb(skb, htt_desc_size_aligned); + if (!desc_ext) + return -ENOMEM; + + desc_ext->info0 = le32_encode_bits(1, HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_FLAG) | + le32_encode_bits(0, HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_TYPE) | + le32_encode_bits(1, + HAL_TX_MSDU_METADATA_INFO0_HOST_TX_DESC_POOL); + + return 0; +} + +static void ath12k_dp_tx_move_payload(struct sk_buff *skb, + unsigned long delta, + bool head) +{ + unsigned long len = skb->len; + + if (head) { + skb_push(skb, delta); + memmove(skb->data, skb->data + delta, len); + skb_trim(skb, len); + } else { + skb_put(skb, delta); + memmove(skb->data + delta, skb->data, len); + skb_pull(skb, delta); + } +} + +static int ath12k_dp_tx_align_payload(struct ath12k_base *ab, + struct sk_buff **pskb) +{ + u32 iova_mask = ab->hw_params->iova_mask; + unsigned long offset, delta1, delta2; + struct sk_buff *skb2, *skb = *pskb; + unsigned int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); + int ret = 0; + + offset = (unsigned long)skb->data & iova_mask; + delta1 = offset; + delta2 = iova_mask - offset + 1; + + if (headroom >= delta1) { + ath12k_dp_tx_move_payload(skb, delta1, true); + } else if (tailroom >= delta2) { + ath12k_dp_tx_move_payload(skb, delta2, false); + } else { + skb2 = skb_realloc_headroom(skb, iova_mask); + if (!skb2) { + ret = -ENOMEM; + goto out; + } + + dev_kfree_skb_any(skb); + + offset = (unsigned long)skb2->data & iova_mask; + if (offset) + ath12k_dp_tx_move_payload(skb2, offset, true); + *pskb = skb2; + } + +out: + return ret; +} + +int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, + struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, + bool is_mcast) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = &ab->dp; @@ -135,9 +232,10 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif, struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); struct hal_tcl_data_cmd *hal_tcl_desc; struct hal_tx_msdu_ext_desc *msg; - struct sk_buff *skb_ext_desc; + struct sk_buff *skb_ext_desc = NULL; struct hal_srng *tcl_ring; struct ieee80211_hdr *hdr = (void *)skb->data; + struct ath12k_vif *ahvif = arvif->ahvif; struct dp_tx_ring *tx_ring; u8 pool_id; u8 hal_ring_id; @@ -145,6 +243,8 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif, u8 ring_selector, ring_map = 0; bool tcl_ring_retry; bool msdu_ext_desc = false; + bool add_htt_metadata = false; + u32 iova_mask = ab->hw_params->iova_mask; if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) return -ESHUTDOWN; @@ -180,7 +280,7 @@ tcl_ring_sel: ti.bank_id = arvif->bank_id; ti.meta_data_flags = arvif->tcl_metadata; - if (arvif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && + if (ahvif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags)) { if (skb_cb->flags & ATH12K_SKB_CIPHER_SET) { ti.encrypt_type = @@ -195,13 +295,27 @@ tcl_ring_sel: msdu_ext_desc = true; } + if (gsn_valid) { + /* Reset and Initialize meta_data_flags with Global Sequence + * Number (GSN) info. + */ + ti.meta_data_flags = + u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM, + HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM); + } + ti.encap_type = ath12k_dp_tx_get_encap_type(arvif, skb); ti.addr_search_flags = arvif->hal_addr_search_flags; ti.search_type = arvif->search_type; ti.type = HAL_TCL_DESC_TYPE_BUFFER; ti.pkt_offset = 0; ti.lmac_id = ar->lmac_id; + ti.vdev_id = arvif->vdev_id; + if (gsn_valid) + ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + ti.bss_ast_hash = arvif->ast_hash; ti.bss_ast_idx = arvif->ast_idx; ti.dscp_tid_tbl_idx = 0; @@ -236,24 +350,54 @@ tcl_ring_sel: default: /* TODO: Take care of other encap modes as well */ ret = -EINVAL; - atomic_inc(&ab->soc_stats.tx_err.misc_fail); + atomic_inc(&ab->device_stats.tx_err.misc_fail); goto fail_remove_tx_buf; } + if (iova_mask && + (unsigned long)skb->data & iova_mask) { + ret = ath12k_dp_tx_align_payload(ab, &skb); + if (ret) { + ath12k_warn(ab, "failed to align TX buffer %d\n", ret); + /* don't bail out, give original buffer + * a chance even unaligned. + */ + goto map; + } + + /* hdr is pointing to a wrong place after alignment, + * so refresh it for later use. + */ + hdr = (void *)skb->data; + } +map: ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); if (dma_mapping_error(ab->dev, ti.paddr)) { - atomic_inc(&ab->soc_stats.tx_err.misc_fail); + atomic_inc(&ab->device_stats.tx_err.misc_fail); ath12k_warn(ab, "failed to DMA map data Tx buffer\n"); ret = -ENOMEM; goto fail_remove_tx_buf; } + if (!test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) && + !(skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) && + !(skb_cb->flags & ATH12K_SKB_CIPHER_SET) && + ieee80211_has_protected(hdr->frame_control)) { + /* Add metadata for sw encrypted vlan group traffic */ + add_htt_metadata = true; + msdu_ext_desc = true; + ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT; + ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW; + ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; + } + tx_desc->skb = skb; tx_desc->mac_id = ar->pdev_idx; ti.desc_id = tx_desc->desc_id; ti.data_len = skb->len; skb_cb->paddr = ti.paddr; - skb_cb->vif = arvif->vif; + skb_cb->vif = ahvif->vif; skb_cb->ar = ar; if (msdu_ext_desc) { @@ -269,18 +413,26 @@ tcl_ring_sel: msg = (struct hal_tx_msdu_ext_desc *)skb_ext_desc->data; ath12k_hal_tx_cmd_ext_desc_setup(ab, msg, &ti); + if (add_htt_metadata) { + ret = ath12k_dp_prepare_htt_metadata(skb_ext_desc); + if (ret < 0) { + ath12k_dbg(ab, ATH12K_DBG_DP_TX, + "Failed to add HTT meta data, dropping packet\n"); + goto fail_free_ext_skb; + } + } + ti.paddr = dma_map_single(ab->dev, skb_ext_desc->data, skb_ext_desc->len, DMA_TO_DEVICE); ret = dma_mapping_error(ab->dev, ti.paddr); - if (ret) { - kfree_skb(skb_ext_desc); - goto fail_unmap_dma; - } + if (ret) + goto fail_free_ext_skb; ti.data_len = skb_ext_desc->len; ti.type = HAL_TCL_DESC_TYPE_EXT_DESC; skb_cb->paddr_ext_desc = ti.paddr; + tx_desc->skb_ext_desc = skb_ext_desc; } hal_ring_id = tx_ring->tcl_data_ring.ring_id; @@ -296,7 +448,7 @@ tcl_ring_sel: * desc because the desc is directly enqueued onto hw queue. */ ath12k_hal_srng_access_end(ab, tcl_ring); - ab->soc_stats.tx_err.desc_na[ti.ring_id]++; + ab->device_stats.tx_err.desc_na[ti.ring_id]++; spin_unlock_bh(&tcl_ring->lock); ret = -ENOMEM; @@ -311,9 +463,22 @@ tcl_ring_sel: ring_selector++; } - goto fail_unmap_dma; + goto fail_unmap_dma_ext; } + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_encap_type[ti.encap_type]++; + arvif->link_stats.tx_encrypt_type[ti.encrypt_type]++; + arvif->link_stats.tx_desc_type[ti.type]++; + + if (is_mcast) + arvif->link_stats.tx_bcast_mcast++; + else + arvif->link_stats.tx_enqueued++; + spin_unlock_bh(&arvif->link_stats_lock); + + ab->device_stats.tx_enqueued[ti.ring_id]++; + ath12k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc, &ti); ath12k_hal_srng_access_end(ab, tcl_ring); @@ -327,16 +492,24 @@ tcl_ring_sel: return 0; -fail_unmap_dma: - dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); - +fail_unmap_dma_ext: if (skb_cb->paddr_ext_desc) dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), + skb_ext_desc->len, DMA_TO_DEVICE); +fail_free_ext_skb: + kfree_skb(skb_ext_desc); + +fail_unmap_dma: + dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); fail_remove_tx_buf: ath12k_dp_tx_release_txbuf(dp, tx_desc, pool_id); + + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_dropped++; + spin_unlock_bh(&arvif->link_stats_lock); + if (tcl_ring_retry) goto tcl_ring_sel; @@ -344,57 +517,84 @@ fail_remove_tx_buf: } static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, - struct sk_buff *msdu, u8 mac_id, - struct dp_tx_ring *tx_ring) + struct dp_tx_ring *tx_ring, + struct ath12k_tx_desc_params *desc_params) { struct ath12k *ar; + struct sk_buff *msdu = desc_params->skb; struct ath12k_skb_cb *skb_cb; - u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); + u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params->mac_id); skb_cb = ATH12K_SKB_CB(msdu); + ar = ab->pdevs[pdev_id].ar; dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } - dev_kfree_skb_any(msdu); + ieee80211_free_txskb(ar->ah->hw, msdu); - ar = ab->pdevs[pdev_id].ar; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); } static void ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, - struct sk_buff *msdu, + struct ath12k_tx_desc_params *desc_params, struct dp_tx_ring *tx_ring, struct ath12k_dp_htt_wbm_tx_status *ts) { struct ieee80211_tx_info *info; + struct ath12k_link_vif *arvif; struct ath12k_skb_cb *skb_cb; + struct ieee80211_vif *vif; + struct ath12k_vif *ahvif; struct ath12k *ar; + struct sk_buff *msdu = desc_params->skb; skb_cb = ATH12K_SKB_CB(msdu); info = IEEE80211_SKB_CB(msdu); ar = skb_cb->ar; + ab->device_stats.tx_completed[tx_ring->tcl_data_ring_id]++; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } + + vif = skb_cb->vif; + if (vif) { + ahvif = ath12k_vif_to_ahvif(vif); + rcu_read_lock(); + arvif = rcu_dereference(ahvif->link[skb_cb->link_id]); + if (arvif) { + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_completed++; + spin_unlock_bh(&arvif->link_stats_lock); + } + rcu_read_unlock(); + } memset(&info->status, 0, sizeof(info->status)); if (ts->acked) { if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ATH12K_DEFAULT_NOISE_FLOOR + - ts->ack_rssi; + info->status.ack_signal = ts->ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) + info->status.ack_signal += ATH12K_DEFAULT_NOISE_FLOOR; + info->status.flags = IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } else { info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; @@ -405,32 +605,33 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, } static void -ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, - void *desc, u8 mac_id, - struct sk_buff *msdu, - struct dp_tx_ring *tx_ring) +ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, void *desc, + struct dp_tx_ring *tx_ring, + struct ath12k_tx_desc_params *desc_params) { struct htt_tx_wbm_completion *status_desc; struct ath12k_dp_htt_wbm_tx_status ts = {0}; enum hal_wbm_htt_tx_comp_status wbm_status; - status_desc = desc + HTT_TX_WBM_COMP_STATUS_OFFSET; + status_desc = desc; wbm_status = le32_get_bits(status_desc->info0, HTT_TX_WBM_COMP_INFO0_STATUS); + ab->device_stats.fw_tx_status[wbm_status]++; switch (wbm_status) { case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: - case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: - case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); ts.ack_rssi = le32_get_bits(status_desc->info2, HTT_TX_WBM_COMP_INFO2_ACK_RSSI); - ath12k_dp_tx_htt_tx_complete_buf(ab, msdu, tx_ring, &ts); + ath12k_dp_tx_htt_tx_complete_buf(ab, desc_params, tx_ring, &ts); break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: - ath12k_dp_tx_free_txbuf(ab, msdu, mac_id, tx_ring); + case HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH: + ath12k_dp_tx_free_txbuf(ab, tx_ring, desc_params); break; case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: /* This event is to be handled only when the driver decides to @@ -438,18 +639,142 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, */ break; default: - ath12k_warn(ab, "Unknown htt tx status %d\n", wbm_status); + ath12k_warn(ab, "Unknown htt wbm tx status %d\n", wbm_status); + break; + } +} + +static void ath12k_dp_tx_update_txcompl(struct ath12k *ar, struct hal_tx_status *ts) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_peer *peer; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + struct rate_info txrate = {0}; + u16 rate, ru_tones; + u8 rate_idx = 0; + int ret; + + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DP_TX, + "failed to find the peer by id %u\n", ts->peer_id); + spin_unlock_bh(&ab->base_lock); + return; + } + sta = peer->sta; + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + + /* This is to prefer choose the real NSS value arsta->last_txrate.nss, + * if it is invalid, then choose the NSS value while assoc. + */ + if (arsta->last_txrate.nss) + txrate.nss = arsta->last_txrate.nss; + else + txrate.nss = arsta->peer_nss; + spin_unlock_bh(&ab->base_lock); + + switch (ts->pkt_type) { + case HAL_TX_RATE_STATS_PKT_TYPE_11A: + case HAL_TX_RATE_STATS_PKT_TYPE_11B: + ret = ath12k_mac_hw_ratecode_to_legacy_rate(ts->mcs, + ts->pkt_type, + &rate_idx, + &rate); + if (ret < 0) { + ath12k_warn(ab, "Invalid tx legacy rate %d\n", ret); + return; + } + + txrate.legacy = rate; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11N: + if (ts->mcs > ATH12K_HT_MCS_MAX) { + ath12k_warn(ab, "Invalid HT mcs index %d\n", ts->mcs); + return; + } + + if (txrate.nss != 0) + txrate.mcs = ts->mcs + 8 * (txrate.nss - 1); + + txrate.flags = RATE_INFO_FLAGS_MCS; + + if (ts->sgi) + txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11AC: + if (ts->mcs > ATH12K_VHT_MCS_MAX) { + ath12k_warn(ab, "Invalid VHT mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + + if (ts->sgi) + txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11AX: + if (ts->mcs > ATH12K_HE_MCS_MAX) { + ath12k_warn(ab, "Invalid HE mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_HE_MCS; + txrate.he_gi = ath12k_he_gi_to_nl80211_he_gi(ts->sgi); + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11BE: + if (ts->mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ab, "Invalid EHT mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_EHT_MCS; + txrate.eht_gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(ts->sgi); break; + default: + ath12k_warn(ab, "Invalid tx pkt type: %d\n", ts->pkt_type); + return; + } + + txrate.bw = ath12k_mac_bw_to_mac80211_bw(ts->bw); + + if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + txrate.bw = RATE_INFO_BW_HE_RU; + ru_tones = ath12k_mac_he_convert_tones_to_ru_tones(ts->tones); + txrate.he_ru_alloc = + ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); } + + if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11BE) { + txrate.bw = RATE_INFO_BW_EHT_RU; + txrate.eht_ru_alloc = + ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(ts->tones); + } + + spin_lock_bh(&ab->base_lock); + arsta->txrate = txrate; + spin_unlock_bh(&ab->base_lock); } static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, - struct sk_buff *msdu, - struct hal_tx_status *ts) + struct ath12k_tx_desc_params *desc_params, + struct hal_tx_status *ts, + int ring) { struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ar->ah; struct ieee80211_tx_info *info; + struct ath12k_link_vif *arvif; struct ath12k_skb_cb *skb_cb; + struct ieee80211_vif *vif; + struct ath12k_vif *ahvif; + struct sk_buff *msdu = desc_params->skb; if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { /* Must not happen */ @@ -457,47 +782,85 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, } skb_cb = ATH12K_SKB_CB(msdu); + ab->device_stats.tx_completed[ring]++; dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } rcu_read_lock(); if (!rcu_dereference(ab->pdevs_active[ar->pdev_idx])) { - dev_kfree_skb_any(msdu); + ieee80211_free_txskb(ah->hw, msdu); goto exit; } if (!skb_cb->vif) { - dev_kfree_skb_any(msdu); + ieee80211_free_txskb(ah->hw, msdu); goto exit; } + vif = skb_cb->vif; + if (vif) { + ahvif = ath12k_vif_to_ahvif(vif); + arvif = rcu_dereference(ahvif->link[skb_cb->link_id]); + if (arvif) { + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_completed++; + spin_unlock_bh(&arvif->link_stats_lock); + } + } + info = IEEE80211_SKB_CB(msdu); memset(&info->status, 0, sizeof(info->status)); /* skip tx rate update from ieee80211_status*/ info->status.rates[0].idx = -1; - if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && - !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ATH12K_DEFAULT_NOISE_FLOOR + - ts->ack_rssi; - info->status.flags = IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; - } + switch (ts->status) { + case HAL_WBM_TQM_REL_REASON_FRAME_ACKED: + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ts->ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) + info->status.ack_signal += ATH12K_DEFAULT_NOISE_FLOOR; - if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX && - (info->flags & IEEE80211_TX_CTL_NO_ACK)) - info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + info->status.flags = IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; + } + break; + case HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX: + if (info->flags & IEEE80211_TX_CTL_NO_ACK) { + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + break; + } + fallthrough; + case HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU: + case HAL_WBM_TQM_REL_REASON_DROP_THRESHOLD: + case HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES: + /* The failure status is due to internal firmware tx failure + * hence drop the frame; do not update the status of frame to + * the upper layer + */ + ieee80211_free_txskb(ah->hw, msdu); + goto exit; + default: + ath12k_dbg(ab, ATH12K_DBG_DP_TX, "tx frame is not acked status %d\n", + ts->status); + break; + } /* NOTE: Tx rate status reporting. Tx completion status does not have * necessary information (for example nss) to build the tx rate. * Might end up reporting it out-of-band from HTT stats. */ + ath12k_dp_tx_update_txcompl(ar, ts); + ieee80211_tx_status_skb(ath12k_ar_to_hw(ar), msdu); exit: @@ -508,6 +871,8 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab, struct hal_wbm_completion_ring_tx *desc, struct hal_tx_status *ts) { + u32 info0 = le32_to_cpu(desc->rate_stats.info0); + ts->buf_rel_source = le32_get_bits(desc->info0, HAL_WBM_COMPL_TX_INFO0_REL_SRC_MODULE); if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && @@ -522,10 +887,17 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab, ts->ppdu_id = le32_get_bits(desc->info1, HAL_WBM_COMPL_TX_INFO1_TQM_STATUS_NUMBER); - if (le32_to_cpu(desc->rate_stats.info0) & HAL_TX_RATE_STATS_INFO0_VALID) - ts->rate_stats = le32_to_cpu(desc->rate_stats.info0); - else - ts->rate_stats = 0; + + ts->peer_id = le32_get_bits(desc->info3, HAL_WBM_COMPL_TX_INFO3_PEER_ID); + + if (info0 & HAL_TX_RATE_STATS_INFO0_VALID) { + ts->pkt_type = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_PKT_TYPE); + ts->mcs = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_MCS); + ts->sgi = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_SGI); + ts->bw = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_BW); + ts->tones = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_TONES_IN_RU); + ts->ofdma = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_OFDMA_TX); + } } void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) @@ -535,12 +907,14 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; struct ath12k_tx_desc_info *tx_desc = NULL; - struct sk_buff *msdu; struct hal_tx_status ts = { 0 }; + struct ath12k_tx_desc_params desc_params; struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; struct hal_wbm_release_ring *desc; - u8 mac_id, pdev_id; + u8 pdev_id; u64 desc_va; + enum hal_wbm_rel_src_module buf_rel_source; + enum hal_wbm_tqm_rel_reason rel_status; spin_lock_bh(&status_ring->lock); @@ -593,28 +967,37 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) continue; } - msdu = tx_desc->skb; - mac_id = tx_desc->mac_id; + desc_params.mac_id = tx_desc->mac_id; + desc_params.skb = tx_desc->skb; + desc_params.skb_ext_desc = tx_desc->skb_ext_desc; + + /* Find the HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE value */ + buf_rel_source = le32_get_bits(tx_status->info0, + HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE); + ab->device_stats.tx_wbm_rel_source[buf_rel_source]++; + + rel_status = le32_get_bits(tx_status->info0, + HAL_WBM_COMPL_TX_INFO0_TQM_RELEASE_REASON); + ab->device_stats.tqm_rel_reason[rel_status]++; /* Release descriptor as soon as extracting necessary info * to reduce contention */ ath12k_dp_tx_release_txbuf(dp, tx_desc, tx_desc->pool_id); if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { - ath12k_dp_tx_process_htt_tx_complete(ab, - (void *)tx_status, - mac_id, msdu, - tx_ring); + ath12k_dp_tx_process_htt_tx_complete(ab, (void *)tx_status, + tx_ring, &desc_params); continue; } - pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); + pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params.mac_id); ar = ab->pdevs[pdev_id].ar; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); - ath12k_dp_tx_complete_msdu(ar, msdu, &ts); + ath12k_dp_tx_complete_msdu(ar, &desc_params, &ts, + tx_ring->tcl_data_ring_id); } } @@ -654,7 +1037,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_BUF: - *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING; + *htt_ring_id = HTT_RX_MON_HOST2MON_BUF_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_STATUS: @@ -662,21 +1045,13 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_DST: - *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING; + *htt_ring_id = HTT_RX_MON_MON2HOST_DEST_RING; *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_DESC: *htt_ring_id = HTT_RXDMA_MONITOR_DESC_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; - case HAL_TX_MONITOR_BUF: - *htt_ring_id = HTT_TX_MON_HOST2MON_BUF_RING; - *htt_ring_type = HTT_SW_TO_HW_RING; - break; - case HAL_TX_MONITOR_DST: - *htt_ring_id = HTT_TX_MON_MON2HOST_DEST_RING; - *htt_ring_type = HTT_HW_TO_SW_RING; - break; default: ath12k_warn(ab, "Unsupported ring type in DP :%d\n", ring_type); ret = -EINVAL; @@ -819,7 +1194,14 @@ int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab) skb_put(skb, len); cmd = (struct htt_ver_req_cmd *)skb->data; cmd->ver_reg_info = le32_encode_bits(HTT_H2T_MSG_TYPE_VERSION_REQ, - HTT_VER_REQ_INFO_MSG_ID); + HTT_OPTION_TAG); + + cmd->tcl_metadata_version = le32_encode_bits(HTT_TAG_TCL_METADATA_VERSION, + HTT_OPTION_TAG) | + le32_encode_bits(HTT_TCL_METADATA_VER_SZ, + HTT_OPTION_LEN) | + le32_encode_bits(HTT_OPTION_TCL_METADATA_VER_V2, + HTT_OPTION_VALUE); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) { @@ -854,7 +1236,7 @@ int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask) int ret; int i; - for (i = 0; i < ab->hw_params->num_rxmda_per_pdev; i++) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; @@ -925,15 +1307,46 @@ int ath12k_dp_tx_htt_rx_filter_setup(struct ath12k_base *ab, u32 ring_id, cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP), HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS); cmd->info0 |= le32_encode_bits(tlv_filter->offset_valid, - HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID); + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID); + cmd->info0 |= + le32_encode_bits(tlv_filter->drop_threshold_valid, + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL); + cmd->info0 |= le32_encode_bits(!tlv_filter->rxmon_disable, + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON); + cmd->info1 = le32_encode_bits(rx_buf_size, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_mgmt, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_ctrl, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_data, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA); cmd->pkt_type_en_flags0 = cpu_to_le32(tlv_filter->pkt_filter_flags0); cmd->pkt_type_en_flags1 = cpu_to_le32(tlv_filter->pkt_filter_flags1); cmd->pkt_type_en_flags2 = cpu_to_le32(tlv_filter->pkt_filter_flags2); cmd->pkt_type_en_flags3 = cpu_to_le32(tlv_filter->pkt_filter_flags3); cmd->rx_filter_tlv = cpu_to_le32(tlv_filter->rx_filter); + cmd->info2 = le32_encode_bits(tlv_filter->rx_drop_threshold, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_mgmt_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_ctrl_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_data_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE); + + cmd->info3 = + le32_encode_bits(tlv_filter->enable_rx_tlv_offset, + HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET); + cmd->info3 |= + le32_encode_bits(tlv_filter->rx_tlv_offset, + HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET); + if (tlv_filter->offset_valid) { cmd->rx_packet_offset = le32_encode_bits(tlv_filter->rx_packet_offset, @@ -1007,6 +1420,7 @@ ath12k_dp_tx_htt_h2t_ext_stats_req(struct ath12k *ar, u8 type, struct htt_ext_stats_cfg_cmd *cmd; int len = sizeof(*cmd); int ret; + u32 pdev_id; skb = ath12k_htc_alloc_skb(ab, len); if (!skb) @@ -1018,7 +1432,8 @@ ath12k_dp_tx_htt_h2t_ext_stats_req(struct ath12k *ar, u8 type, memset(cmd, 0, sizeof(*cmd)); cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_EXT_STATS_CFG; - cmd->hdr.pdev_mask = 1 << ar->pdev->pdev_id; + pdev_id = ath12k_mac_get_target_pdev_id(ar); + cmd->hdr.pdev_mask = 1 << pdev_id; cmd->hdr.stats_type = type; cmd->cfg_param0 = cpu_to_le32(cfg_params->cfg0); @@ -1044,13 +1459,7 @@ int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset) struct ath12k_base *ab = ar->ab; int ret; - ret = ath12k_dp_tx_htt_tx_monitor_mode_ring_config(ar, reset); - if (ret) { - ath12k_err(ab, "failed to setup tx monitor filter %d\n", ret); - return ret; - } - - ret = ath12k_dp_tx_htt_tx_monitor_mode_ring_config(ar, reset); + ret = ath12k_dp_tx_htt_rx_monitor_mode_ring_config(ar, reset); if (ret) { ath12k_err(ab, "failed to setup rx monitor filter %d\n", ret); return ret; @@ -1062,15 +1471,28 @@ int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset) int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) { struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; struct htt_rx_ring_tlv_filter tlv_filter = {0}; - int ret, ring_id; + int ret, ring_id, i; - ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; tlv_filter.offset_valid = false; if (!reset) { - tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING; + tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING; + + tlv_filter.drop_threshold_valid = true; + tlv_filter.rx_drop_threshold = HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE; + + tlv_filter.enable_log_mgmt_type = true; + tlv_filter.enable_log_ctrl_type = true; + tlv_filter.enable_log_data_type = true; + + tlv_filter.conf_len_ctrl = HTT_RX_RING_DEFAULT_DMA_LENGTH; + tlv_filter.conf_len_mgmt = HTT_RX_RING_DEFAULT_DMA_LENGTH; + tlv_filter.conf_len_data = HTT_RX_RING_DEFAULT_DMA_LENGTH; + + tlv_filter.enable_rx_tlv_offset = true; + tlv_filter.rx_tlv_offset = HTT_RX_RING_PKT_TLV_OFFSET; + tlv_filter.pkt_filter_flags0 = HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 | HTT_RX_MON_MO_MGMT_FILTER_FLAGS0; @@ -1085,16 +1507,64 @@ int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) HTT_RX_MON_MO_CTRL_FILTER_FLASG3 | HTT_RX_MON_FP_DATA_FILTER_FLASG3 | HTT_RX_MON_MO_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath12k_mac_mon_status_filter_default; + + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) + tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar); } if (ab->hw_params->rxdma1_enable) { - ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, 0, - HAL_RXDMA_MONITOR_BUF, - DP_RXDMA_REFILL_RING_SIZE, + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for monitor buf %d\n", + ret); + return ret; + } + } + return 0; + } + + if (!reset) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ab->dp.rx_mac_buf_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, + i, + HAL_RXDMA_BUF, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for mon rx buf %d\n", + ret); + return ret; + } + } + } + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ab->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + if (!reset) { + tlv_filter.rx_filter = + HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; + } + + ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, + i, + HAL_RXDMA_MONITOR_STATUS, + RX_MON_STATUS_BUF_SIZE, &tlv_filter); if (ret) { ath12k_err(ab, - "failed to setup filter for monitor buf %d\n", ret); + "failed to setup filter for mon status buf %d\n", + ret); return ret; } } @@ -1209,31 +1679,3 @@ err_free: dev_kfree_skb_any(skb); return ret; } - -int ath12k_dp_tx_htt_tx_monitor_mode_ring_config(struct ath12k *ar, bool reset) -{ - struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; - struct htt_tx_ring_tlv_filter tlv_filter = {0}; - int ret, ring_id; - - ring_id = dp->tx_mon_buf_ring.refill_buf_ring.ring_id; - - /* TODO: Need to set upstream/downstream tlv filters - * here - */ - - if (ab->hw_params->rxdma1_enable) { - ret = ath12k_dp_tx_htt_tx_filter_setup(ar->ab, ring_id, 0, - HAL_TX_MONITOR_BUF, - DP_RXDMA_REFILL_RING_SIZE, - &tlv_filter); - if (ret) { - ath12k_err(ab, - "failed to setup filter for monitor buf %d\n", ret); - return ret; - } - } - - return 0; -} diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h b/drivers/net/wireless/ath/ath12k/dp_tx.h index 436d77e5e9ee..10acdcf1fa8f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/dp_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_TX_H @@ -12,12 +12,13 @@ struct ath12k_dp_htt_wbm_tx_status { bool acked; - int ack_rssi; + s8 ack_rssi; }; int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab); -int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif, - struct sk_buff *skb); +int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, + struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, + bool is_mcast); void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id); int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask); @@ -36,6 +37,5 @@ int ath12k_dp_tx_htt_tx_filter_setup(struct ath12k_base *ab, u32 ring_id, int mac_id, enum hal_ring_type ring_type, int tx_buf_size, struct htt_tx_ring_tlv_filter *htt_tlv_filter); -int ath12k_dp_tx_htt_tx_monitor_mode_ring_config(struct ath12k *ar, bool reset); int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset); #endif diff --git a/drivers/net/wireless/ath/ath12k/fw.c b/drivers/net/wireless/ath/ath12k/fw.c index 5be4b2d4a19d..5ac497f80cad 100644 --- a/drivers/net/wireless/ath/ath12k/fw.c +++ b/drivers/net/wireless/ath/ath12k/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -99,6 +99,8 @@ static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab, __set_bit(i, ab->fw.fw_features); } + ab->fw.fw_features_valid = true; + ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "", ab->fw.fw_features, sizeof(ab->fw.fw_features)); @@ -169,3 +171,8 @@ void ath12k_fw_unmap(struct ath12k_base *ab) release_firmware(ab->fw.fw); memset(&ab->fw, 0, sizeof(ab->fw)); } + +bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat) +{ + return ab->fw.fw_features_valid && test_bit(feat, ab->fw.fw_features); +} diff --git a/drivers/net/wireless/ath/ath12k/fw.h b/drivers/net/wireless/ath/ath12k/fw.h index 3ff041f15fa0..7afaefed5086 100644 --- a/drivers/net/wireless/ath/ath12k/fw.h +++ b/drivers/net/wireless/ath/ath12k/fw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_FW_H @@ -23,11 +23,15 @@ enum ath12k_fw_features { */ ATH12K_FW_FEATURE_MULTI_QRTR_ID = 0, + /* The firmware supports MLO capability */ + ATH12K_FW_FEATURE_MLO, + /* keep last */ ATH12K_FW_FEATURE_COUNT, }; void ath12k_fw_map(struct ath12k_base *ab); void ath12k_fw_unmap(struct ath12k_base *ab); +bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat); #endif /* ATH12K_FW_H */ diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index 78310da8cfe8..a301898e5849 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/dma-mapping.h> #include "hal_tx.h" @@ -154,7 +154,14 @@ static const struct hal_srng_config hw_srng_config_template[] = { .ring_dir = HAL_SRNG_DIR_SRC, .max_size = HAL_RXDMA_RING_MAX_SIZE_BE, }, - [HAL_RXDMA_MONITOR_STATUS] = { 0, }, + [HAL_RXDMA_MONITOR_STATUS] = { + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .mac_type = ATH12K_HAL_SRNG_PMAC, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE_BE, + }, [HAL_RXDMA_MONITOR_DESC] = { 0, }, [HAL_RXDMA_DIR_BUF] = { .start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF, @@ -181,7 +188,7 @@ static const struct hal_srng_config hw_srng_config_template[] = { .max_size = HAL_WBM2PPE_RELEASE_RING_BASE_MSB_RING_SIZE, }, [HAL_TX_MONITOR_BUF] = { - .start_ring_id = HAL_SRNG_SW2TXMON_BUF0, + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2TXMON_BUF0, .max_rings = 1, .entry_size = sizeof(struct hal_mon_buf_ring) >> 2, .mac_type = ATH12K_HAL_SRNG_PMAC, @@ -385,13 +392,13 @@ static u8 ath12k_hw_qcn9274_rx_desc_get_msdu_pkt_type(struct hal_rx_desc *desc) static u8 ath12k_hw_qcn9274_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.qcn9274.msdu_end.info12, - RX_MSDU_END_QCN9274_INFO12_MIMO_SS_BITMAP); + RX_MSDU_END_INFO12_MIMO_SS_BITMAP); } static u8 ath12k_hw_qcn9274_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) { return le16_get_bits(desc->u.qcn9274.msdu_end.info5, - RX_MSDU_END_QCN9274_INFO5_TID); + RX_MSDU_END_INFO5_TID); } static u16 ath12k_hw_qcn9274_rx_desc_get_mpdu_peer_id(struct hal_rx_desc *desc) @@ -449,8 +456,8 @@ static u8 *ath12k_hw_qcn9274_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) static bool ath12k_hw_qcn9274_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) { - return __le32_to_cpu(desc->u.qcn9274.mpdu_start.info6) & - RX_MPDU_START_INFO6_MCAST_BCAST; + return __le16_to_cpu(desc->u.qcn9274.msdu_end.info5) & + RX_MSDU_END_INFO5_DA_IS_MCBC; } static void ath12k_hw_qcn9274_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, @@ -511,11 +518,6 @@ static void ath12k_hw_qcn9274_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9274.mpdu_start.pn[1]); } -static u16 ath12k_hw_qcn9274_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) -{ - return __le16_to_cpu(desc->u.qcn9274.mpdu_start.frame_ctrl); -} - static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) { struct ath12k_hal *hal = &ab->hal; @@ -552,9 +554,9 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP; s = &hal->srng_config[HAL_TCL_DATA]; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB; + 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 - HAL_TCL1_RING_BASE_LSB; + 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]; @@ -566,29 +568,29 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; s = &hal->srng_config[HAL_CE_SRC]; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; + 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->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + 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->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + 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 + HAL_CE_DST_STATUS_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + 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); @@ -736,7 +738,6 @@ const struct hal_rx_ops hal_rx_qcn9274_ops = { .rx_desc_is_da_mcbc = ath12k_hw_qcn9274_rx_desc_is_da_mcbc, .rx_desc_get_dot11_hdr = ath12k_hw_qcn9274_rx_desc_get_dot11_hdr, .rx_desc_get_crypto_header = ath12k_hw_qcn9274_rx_desc_get_crypto_hdr, - .rx_desc_get_mpdu_frame_ctl = ath12k_hw_qcn9274_rx_desc_get_mpdu_frame_ctl, .dp_rx_h_msdu_done = ath12k_hw_qcn9274_dp_rx_h_msdu_done, .dp_rx_h_l4_cksum_fail = ath12k_hw_qcn9274_dp_rx_h_l4_cksum_fail, .dp_rx_h_ip_cksum_fail = ath12k_hw_qcn9274_dp_rx_h_ip_cksum_fail, @@ -846,13 +847,13 @@ static u8 ath12k_hw_qcn9274_compact_rx_desc_get_msdu_pkt_type(struct hal_rx_desc static u8 ath12k_hw_qcn9274_compact_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.qcn9274_compact.msdu_end.info12, - RX_MSDU_END_QCN9274_INFO12_MIMO_SS_BITMAP); + RX_MSDU_END_INFO12_MIMO_SS_BITMAP); } static u8 ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) { return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, - RX_MSDU_END_QCN9274_INFO5_TID); + RX_MSDU_END_INFO5_TID); } static u16 ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_peer_id(struct hal_rx_desc *desc) @@ -908,8 +909,8 @@ static u8 *ath12k_hw_qcn9274_compact_rx_desc_mpdu_start_addr2(struct hal_rx_desc static bool ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) { - return __le32_to_cpu(desc->u.qcn9274_compact.mpdu_start.info6) & - RX_MPDU_START_INFO6_MCAST_BCAST; + return __le16_to_cpu(desc->u.qcn9274_compact.msdu_end.info5) & + RX_MSDU_END_INFO5_DA_IS_MCBC; } static void ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, @@ -975,11 +976,6 @@ ath12k_hw_qcn9274_compact_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9274_compact.mpdu_start.pn[1]); } -static u16 ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) -{ - return __le16_to_cpu(desc->u.qcn9274_compact.mpdu_start.frame_ctrl); -} - static bool ath12k_hw_qcn9274_compact_dp_rx_h_msdu_done(struct hal_rx_desc *desc) { return !!le32_get_bits(desc->u.qcn9274_compact.msdu_end.info14, @@ -1080,8 +1076,6 @@ const struct hal_rx_ops hal_rx_qcn9274_compact_ops = { .rx_desc_is_da_mcbc = ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc, .rx_desc_get_dot11_hdr = ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr, .rx_desc_get_crypto_header = ath12k_hw_qcn9274_compact_rx_desc_get_crypto_hdr, - .rx_desc_get_mpdu_frame_ctl = - ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_frame_ctl, .dp_rx_h_msdu_done = ath12k_hw_qcn9274_compact_dp_rx_h_msdu_done, .dp_rx_h_l4_cksum_fail = ath12k_hw_qcn9274_compact_dp_rx_h_l4_cksum_fail, .dp_rx_h_ip_cksum_fail = ath12k_hw_qcn9274_compact_dp_rx_h_ip_cksum_fail, @@ -1198,7 +1192,7 @@ static u8 ath12k_hw_wcn7850_rx_desc_get_msdu_pkt_type(struct hal_rx_desc *desc) static u8 ath12k_hw_wcn7850_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.wcn7850.msdu_end.info12, - RX_MSDU_END_WCN7850_INFO12_MIMO_SS_BITMAP); + RX_MSDU_END_INFO12_MIMO_SS_BITMAP); } static u8 ath12k_hw_wcn7850_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) @@ -1216,7 +1210,7 @@ static void ath12k_hw_wcn7850_rx_desc_copy_end_tlv(struct hal_rx_desc *fdesc, struct hal_rx_desc *ldesc) { memcpy(&fdesc->u.wcn7850.msdu_end, &ldesc->u.wcn7850.msdu_end, - sizeof(struct rx_msdu_end_wcn7850)); + sizeof(struct rx_msdu_end_qcn9274)); } static u32 ath12k_hw_wcn7850_rx_desc_get_mpdu_start_tag(struct hal_rx_desc *desc) @@ -1330,11 +1324,6 @@ static void ath12k_hw_wcn7850_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.wcn7850.mpdu_start.pn[1]); } -static u16 ath12k_hw_wcn7850_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) -{ - return __le16_to_cpu(desc->u.wcn7850.mpdu_start.frame_ctrl); -} - static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab) { struct ath12k_hal *hal = &ab->hal; @@ -1371,9 +1360,9 @@ static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab) 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; + 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 - HAL_TCL1_RING_BASE_LSB; + 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]; @@ -1386,31 +1375,31 @@ static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab) s = &hal->srng_config[HAL_CE_SRC]; s->max_rings = 12; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; + 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 + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + 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 + + 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 + HAL_CE_DST_STATUS_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + 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); @@ -1555,7 +1544,6 @@ const struct hal_rx_ops hal_rx_wcn7850_ops = { .rx_desc_is_da_mcbc = ath12k_hw_wcn7850_rx_desc_is_da_mcbc, .rx_desc_get_dot11_hdr = ath12k_hw_wcn7850_rx_desc_get_dot11_hdr, .rx_desc_get_crypto_header = ath12k_hw_wcn7850_rx_desc_get_crypto_hdr, - .rx_desc_get_mpdu_frame_ctl = ath12k_hw_wcn7850_rx_desc_get_mpdu_frame_ctl, .dp_rx_h_msdu_done = ath12k_hw_wcn7850_dp_rx_h_msdu_done, .dp_rx_h_l4_cksum_fail = ath12k_hw_wcn7850_dp_rx_h_l4_cksum_fail, .dp_rx_h_ip_cksum_fail = ath12k_hw_wcn7850_dp_rx_h_ip_cksum_fail, @@ -1756,7 +1744,7 @@ static void ath12k_hal_srng_src_hw_init(struct ath12k_base *ab, HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB) | u32_encode_bits((srng->entry_size * srng->num_entries), HAL_TCL1_RING_BASE_MSB_RING_SIZE); - ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET, val); + ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET(ab), val); val = u32_encode_bits(srng->entry_size, HAL_REO1_RING_ID_ENTRY_SIZE); ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET(ab), val); @@ -1962,21 +1950,22 @@ u32 ath12k_hal_ce_dst_status_get_length(struct hal_ce_srng_dst_status_desc *desc { u32 len; - len = le32_get_bits(desc->flags, HAL_CE_DST_STATUS_DESC_FLAGS_LEN); + len = le32_get_bits(READ_ONCE(desc->flags), HAL_CE_DST_STATUS_DESC_FLAGS_LEN); desc->flags &= ~cpu_to_le32(HAL_CE_DST_STATUS_DESC_FLAGS_LEN); return len; } void ath12k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie, - dma_addr_t paddr) + dma_addr_t paddr, + enum hal_rx_buf_return_buf_manager rbm) { desc->buf_addr_info.info0 = le32_encode_bits((paddr & HAL_ADDR_LSB_REG_MASK), BUFFER_ADDR_INFO0_ADDR); desc->buf_addr_info.info1 = le32_encode_bits(((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT), BUFFER_ADDR_INFO1_ADDR) | - le32_encode_bits(1, BUFFER_ADDR_INFO1_RET_BUF_MGR) | + le32_encode_bits(rbm, BUFFER_ADDR_INFO1_RET_BUF_MGR) | le32_encode_bits(cookie, BUFFER_ADDR_INFO1_SW_COOKIE); } @@ -2053,6 +2042,24 @@ int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng, return ((srng->ring_size - hp + tp) / srng->entry_size) - 1; } +void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab, + struct hal_srng *srng) +{ + void *desc; + u32 next_hp; + + lockdep_assert_held(&srng->lock); + + next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; + + if (next_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + next_hp; + + return desc; +} + void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng) { @@ -2086,6 +2093,17 @@ void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, return desc; } +void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (((srng->u.src_ring.hp + srng->entry_size) % srng->ring_size) == + srng->u.src_ring.cached_tp) + return NULL; + + return srng->ring_base_vaddr + srng->u.src_ring.hp; +} + void *ath12k_hal_srng_src_reap_next(struct ath12k_base *ab, struct hal_srng *srng) { @@ -2131,7 +2149,7 @@ void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng) srng->u.src_ring.cached_tp = *(volatile u32 *)srng->u.src_ring.tp_addr; else - srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = READ_ONCE(*srng->u.dst_ring.hp_addr); } /* Update cached ring head/tail pointers to HW. ath12k_hal_srng_access_begin() diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index 107927d64bbb..c1750b5dc03c 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HAL_H @@ -11,6 +11,7 @@ #include "rx_desc.h" struct ath12k_base; +#define HAL_CE_REMAP_REG_BASE (ab->ce_remap_base_addr) #define HAL_LINK_DESC_SIZE (32 << 2) #define HAL_LINK_DESC_ALIGN 128 @@ -21,6 +22,7 @@ struct ath12k_base; #define HAL_MAX_AVAIL_BLK_RES 3 #define HAL_RING_BASE_ALIGN 8 +#define HAL_REO_QLUT_ADDR_ALIGN 256 #define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704 /* TODO: Check with hw team on the supported scatter buf size */ @@ -39,15 +41,21 @@ struct ath12k_base; #define HAL_OFFSET_FROM_HP_TO_TP 4 #define HAL_SHADOW_REG(x) (HAL_SHADOW_BASE_ADDR + (4 * (x))) +#define HAL_REO_QDESC_MAX_PEERID 8191 /* WCSS Relative address */ +#define HAL_SEQ_WCSS_CMEM_OFFSET 0x00100000 #define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000 #define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000 #define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000 -#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG 0x01b80000 -#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG 0x01b81000 -#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG 0x01b82000 -#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG 0x01b83000 +#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce0_src_reg_base) +#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce0_dest_reg_base) +#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce1_src_reg_base) +#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce1_dest_reg_base) #define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000 #define HAL_CE_WFSS_CE_REG_BASE 0x01b80000 @@ -57,8 +65,10 @@ struct ath12k_base; /* SW2TCL(x) R0 ring configuration address */ #define HAL_TCL1_RING_CMN_CTRL_REG 0x00000020 #define HAL_TCL1_RING_DSCP_TID_MAP 0x00000240 -#define HAL_TCL1_RING_BASE_LSB 0x00000900 -#define HAL_TCL1_RING_BASE_MSB 0x00000904 +#define HAL_TCL1_RING_BASE_LSB(ab) \ + ((ab)->hw_params->regs->hal_tcl1_ring_base_lsb) +#define HAL_TCL1_RING_BASE_MSB(ab) \ + ((ab)->hw_params->regs->hal_tcl1_ring_base_msb) #define HAL_TCL1_RING_ID(ab) ((ab)->hw_params->regs->hal_tcl1_ring_id) #define HAL_TCL1_RING_MISC(ab) \ ((ab)->hw_params->regs->hal_tcl1_ring_misc) @@ -76,30 +86,31 @@ struct ath12k_base; ((ab)->hw_params->regs->hal_tcl1_ring_msi1_base_msb) #define HAL_TCL1_RING_MSI1_DATA(ab) \ ((ab)->hw_params->regs->hal_tcl1_ring_msi1_data) -#define HAL_TCL2_RING_BASE_LSB 0x00000978 +#define HAL_TCL2_RING_BASE_LSB(ab) \ + ((ab)->hw_params->regs->hal_tcl2_ring_base_lsb) #define HAL_TCL_RING_BASE_LSB(ab) \ ((ab)->hw_params->regs->hal_tcl_ring_base_lsb) -#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) \ - (HAL_TCL1_RING_MSI1_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) \ - (HAL_TCL1_RING_MSI1_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) \ - (HAL_TCL1_RING_MSI1_DATA(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_BASE_MSB_OFFSET \ - (HAL_TCL1_RING_BASE_MSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_ID_OFFSET(ab) \ - (HAL_TCL1_RING_ID(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) \ - (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) \ - (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) \ - (HAL_TCL1_RING_TP_ADDR_LSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) \ - (HAL_TCL1_RING_TP_ADDR_MSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MISC_OFFSET(ab) \ - (HAL_TCL1_RING_MISC(ab) - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MSI1_BASE_LSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MSI1_BASE_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MSI1_DATA(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_BASE_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_BASE_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_ID_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_ID(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_TP_ADDR_LSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_TP_ADDR_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_MISC_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MISC(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) /* SW2TCL(x) R2 ring pointers (head/tail) address */ #define HAL_TCL1_RING_HP 0x00002000 @@ -132,6 +143,8 @@ struct ath12k_base; #define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008 #define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c #define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010 +#define HAL_REO1_QDESC_ADDR(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_addr) +#define HAL_REO1_QDESC_MAX_PEERID(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_max_peerid) #define HAL_REO1_SW_COOKIE_CFG0(ab) ((ab)->hw_params->regs->hal_reo1_sw_cookie_cfg0) #define HAL_REO1_SW_COOKIE_CFG1(ab) ((ab)->hw_params->regs->hal_reo1_sw_cookie_cfg1) #define HAL_REO1_QDESC_LUT_BASE0(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_lut_base0) @@ -319,6 +332,8 @@ struct ath12k_base; #define HAL_REO1_SW_COOKIE_CFG_ALIGN BIT(18) #define HAL_REO1_SW_COOKIE_CFG_ENABLE BIT(19) #define HAL_REO1_SW_COOKIE_CFG_GLOBAL_ENABLE BIT(20) +#define HAL_REO_QDESC_ADDR_READ_LUT_ENABLE BIT(7) +#define HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY BIT(6) /* CE ring bit field mask and shift */ #define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0) @@ -365,6 +380,9 @@ struct ath12k_base; * ath12k_hal_rx_desc_get_err(). */ +#define HAL_IPQ5332_CE_WFSS_REG_BASE 0x740000 +#define HAL_IPQ5332_CE_SIZE 0x100000 + enum hal_srng_ring_id { HAL_SRNG_RING_ID_REO2SW0 = 0, HAL_SRNG_RING_ID_REO2SW1, @@ -480,13 +498,14 @@ enum hal_srng_ring_id { HAL_SRNG_RING_ID_WMAC1_SW2RXMON_BUF0 = HAL_SRNG_RING_ID_PMAC1_ID_START, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, HAL_SRNG_RING_ID_WMAC1_RXMON2SW0 = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, HAL_SRNG_RING_ID_RXDMA_DIR_BUF, - HAL_SRNG_RING_ID_WMAC1_SW2TXMON_BUF0, HAL_SRNG_RING_ID_WMAC1_TXMON2SW0_BUF0, + HAL_SRNG_RING_ID_WMAC1_SW2TXMON_BUF0, HAL_SRNG_RING_ID_PMAC1_ID_END, }; @@ -566,7 +585,8 @@ enum hal_reo_cmd_type { * or cache was blocked * @HAL_REO_CMD_FAILED: Command execution failed, could be due to * invalid queue desc - * @HAL_REO_CMD_RESOURCE_BLOCKED: + * @HAL_REO_CMD_RESOURCE_BLOCKED: Command could not be executed because + * one or more descriptors were blocked * @HAL_REO_CMD_DRAIN: */ enum hal_reo_cmd_status { @@ -767,15 +787,15 @@ struct hal_srng_config { }; /** - * enum hal_rx_buf_return_buf_manager + * enum hal_rx_buf_return_buf_manager - manager for returned rx buffers * * @HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST: Buffer returned to WBM idle buffer list - * @HAL_RX_BUF_RBM_WBM_CHIP0_IDLE_DESC_LIST: Descriptor returned to WBM idle - * descriptor list, where the chip 0 WBM is chosen in case of a multi-chip config - * @HAL_RX_BUF_RBM_WBM_CHIP1_IDLE_DESC_LIST: Descriptor returned to WBM idle - * descriptor list, where the chip 1 WBM is chosen in case of a multi-chip config - * @HAL_RX_BUF_RBM_WBM_CHIP2_IDLE_DESC_LIST: Descriptor returned to WBM idle - * descriptor list, where the chip 2 WBM is chosen in case of a multi-chip config + * @HAL_RX_BUF_RBM_WBM_DEV0_IDLE_DESC_LIST: Descriptor returned to WBM idle + * descriptor list, where the device 0 WBM is chosen in case of a multi-device config + * @HAL_RX_BUF_RBM_WBM_DEV1_IDLE_DESC_LIST: Descriptor returned to WBM idle + * descriptor list, where the device 1 WBM is chosen in case of a multi-device config + * @HAL_RX_BUF_RBM_WBM_DEV2_IDLE_DESC_LIST: Descriptor returned to WBM idle + * descriptor list, where the device 2 WBM is chosen in case of a multi-device config * @HAL_RX_BUF_RBM_FW_BM: Buffer returned to FW * @HAL_RX_BUF_RBM_SW0_BM: For ring 0 -- returned to host * @HAL_RX_BUF_RBM_SW1_BM: For ring 1 -- returned to host @@ -788,9 +808,9 @@ struct hal_srng_config { enum hal_rx_buf_return_buf_manager { HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST, - HAL_RX_BUF_RBM_WBM_CHIP0_IDLE_DESC_LIST, - HAL_RX_BUF_RBM_WBM_CHIP1_IDLE_DESC_LIST, - HAL_RX_BUF_RBM_WBM_CHIP2_IDLE_DESC_LIST, + HAL_RX_BUF_RBM_WBM_DEV0_IDLE_DESC_LIST, + HAL_RX_BUF_RBM_WBM_DEV1_IDLE_DESC_LIST, + HAL_RX_BUF_RBM_WBM_DEV2_IDLE_DESC_LIST, HAL_RX_BUF_RBM_FW_BM, HAL_RX_BUF_RBM_SW0_BM, HAL_RX_BUF_RBM_SW1_BM, @@ -1068,7 +1088,6 @@ struct hal_rx_ops { bool (*rx_desc_is_da_mcbc)(struct hal_rx_desc *desc); void (*rx_desc_get_dot11_hdr)(struct hal_rx_desc *desc, struct ieee80211_hdr *hdr); - u16 (*rx_desc_get_mpdu_frame_ctl)(struct hal_rx_desc *desc); void (*rx_desc_get_crypto_header)(struct hal_rx_desc *desc, u8 *crypto_hdr, enum hal_encrypt_type enctype); @@ -1113,7 +1132,8 @@ dma_addr_t ath12k_hal_srng_get_tp_addr(struct ath12k_base *ab, dma_addr_t ath12k_hal_srng_get_hp_addr(struct ath12k_base *ab, struct hal_srng *srng); void ath12k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie, - dma_addr_t paddr); + dma_addr_t paddr, + enum hal_rx_buf_return_buf_manager rbm); u32 ath12k_hal_ce_get_desc_size(enum hal_ce_desc type); void ath12k_hal_ce_src_set_desc(struct hal_ce_srng_src_desc *desc, dma_addr_t paddr, u32 len, u32 id, u8 byte_swap_data); @@ -1125,6 +1145,7 @@ void ath12k_hal_srng_get_params(struct ath12k_base *ab, struct hal_srng *srng, struct hal_srng_params *params); void *ath12k_hal_srng_dst_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng); +void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng); void *ath12k_hal_srng_dst_peek(struct ath12k_base *ab, struct hal_srng *srng); int ath12k_hal_srng_dst_num_free(struct ath12k_base *ab, struct hal_srng *srng, bool sync_hw_ptr); @@ -1132,6 +1153,8 @@ void *ath12k_hal_srng_src_get_next_reaped(struct ath12k_base *ab, struct hal_srng *srng); void *ath12k_hal_srng_src_reap_next(struct ath12k_base *ab, struct hal_srng *srng); +void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab, + struct hal_srng *srng); void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng); int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng, @@ -1153,4 +1176,5 @@ int ath12k_hal_srng_update_shadow_config(struct ath12k_base *ab, void ath12k_hal_srng_shadow_config(struct ath12k_base *ab); void ath12k_hal_srng_shadow_update_hp_tp(struct ath12k_base *ab, struct hal_srng *srng); +void ath12k_hal_reo_shared_qaddr_cache_clear(struct ath12k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 63340256d3f6..0173f731bfef 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -522,7 +522,7 @@ enum hal_tlv_tag { HAL_PHYRXHT_SIG_USR_SU = 468 /* 0x1d4 */, HAL_PHYRXHT_SIG_USR_MU_MIMO = 469 /* 0x1d5 */, HAL_PHYRX_GENERIC_U_SIG = 470 /* 0x1d6 */, - HAL_PHYRX_GENERICHT_SIG = 471 /* 0x1d7 */, + HAL_PHYRX_GENERIC_EHT_SIG = 471 /* 0x1d7 */, HAL_OVERWRITE_RESP_START = 472 /* 0x1d8 */, HAL_OVERWRITE_RESP_PREAMBLE_INFO = 473 /* 0x1d9 */, HAL_OVERWRITE_RESP_FRAME_INFO = 474 /* 0x1da */, @@ -579,9 +579,11 @@ struct hal_tlv_hdr { #define HAL_TLV_64_HDR_TAG GENMASK(9, 1) #define HAL_TLV_64_HDR_LEN GENMASK(21, 10) +#define HAL_TLV_64_USR_ID GENMASK(31, 26) +#define HAL_TLV_64_ALIGN 8 struct hal_tlv_64_hdr { - u64 tl; + __le64 tl; u8 value[]; } __packed; @@ -597,8 +599,30 @@ struct hal_tlv_64_hdr { #define RX_MPDU_DESC_INFO0_MPDU_QOS_CTRL_VALID BIT(27) #define RX_MPDU_DESC_INFO0_TID GENMASK(31, 28) -/* TODO revisit after meta data is concluded */ -#define RX_MPDU_DESC_META_DATA_PEER_ID GENMASK(15, 0) +/* Peer Metadata classification */ + +/* Version 0 */ +#define RX_MPDU_DESC_META_DATA_V0_PEER_ID GENMASK(15, 0) +#define RX_MPDU_DESC_META_DATA_V0_VDEV_ID GENMASK(23, 16) + +/* Version 1 */ +#define RX_MPDU_DESC_META_DATA_V1_PEER_ID GENMASK(13, 0) +#define RX_MPDU_DESC_META_DATA_V1_LOGICAL_LINK_ID GENMASK(15, 14) +#define RX_MPDU_DESC_META_DATA_V1_VDEV_ID GENMASK(23, 16) +#define RX_MPDU_DESC_META_DATA_V1_LMAC_ID GENMASK(25, 24) +#define RX_MPDU_DESC_META_DATA_V1_DEVICE_ID GENMASK(28, 26) + +/* Version 1A */ +#define RX_MPDU_DESC_META_DATA_V1A_PEER_ID GENMASK(13, 0) +#define RX_MPDU_DESC_META_DATA_V1A_VDEV_ID GENMASK(21, 14) +#define RX_MPDU_DESC_META_DATA_V1A_LOGICAL_LINK_ID GENMASK(25, 22) +#define RX_MPDU_DESC_META_DATA_V1A_DEVICE_ID GENMASK(28, 26) + +/* Version 1B */ +#define RX_MPDU_DESC_META_DATA_V1B_PEER_ID GENMASK(13, 0) +#define RX_MPDU_DESC_META_DATA_V1B_VDEV_ID GENMASK(21, 14) +#define RX_MPDU_DESC_META_DATA_V1B_HW_LINK_ID GENMASK(25, 22) +#define RX_MPDU_DESC_META_DATA_V1B_DEVICE_ID GENMASK(28, 26) struct rx_mpdu_desc { __le32 info0; /* %RX_MPDU_DESC_INFO */ @@ -683,7 +707,7 @@ enum hal_rx_msdu_desc_reo_dest_ind { #define RX_MSDU_DESC_INFO0_DECAP_FORMAT GENMASK(30, 29) #define HAL_RX_MSDU_PKT_LENGTH_GET(val) \ - (u32_get_bits((val), RX_MSDU_DESC_INFO0_MSDU_LENGTH)) + (le32_get_bits((val), RX_MSDU_DESC_INFO0_MSDU_LENGTH)) struct rx_msdu_desc { __le32 info0; @@ -984,6 +1008,10 @@ enum hal_reo_entr_rxdma_ecode { HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR, HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR, HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_FRAG_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MULTICAST_ECHO_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_MISMATCH_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_GRPCAST_AMSDU_WDS_ERR, HAL_REO_ENTR_RING_RXDMA_ECODE_MAX, }; @@ -1260,11 +1288,13 @@ enum hal_tcl_encap_type { HAL_TCL_ENCAP_TYPE_NATIVE_WIFI, HAL_TCL_ENCAP_TYPE_ETHERNET, HAL_TCL_ENCAP_TYPE_802_3 = 3, + HAL_TCL_ENCAP_TYPE_MAX }; enum hal_tcl_desc_type { HAL_TCL_DESC_TYPE_BUFFER, HAL_TCL_DESC_TYPE_EXT_DESC, + HAL_TCL_DESC_TYPE_MAX, }; enum hal_wbm_htt_tx_comp_status { @@ -1274,6 +1304,7 @@ enum hal_wbm_htt_tx_comp_status { HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ, HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT, HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY, + HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH, HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX, }; @@ -1782,6 +1813,7 @@ enum hal_wbm_rel_src_module { HAL_WBM_REL_SRC_MODULE_REO, HAL_WBM_REL_SRC_MODULE_FW, HAL_WBM_REL_SRC_MODULE_SW, + HAL_WBM_REL_SRC_MODULE_MAX, }; enum hal_wbm_rel_desc_type { @@ -1974,6 +2006,7 @@ struct hal_wbm_release_ring_cc_rx { #define HAL_WBM_RELEASE_INFO3_CONTINUATION BIT(2) #define HAL_WBM_RELEASE_INFO5_LOOPING_COUNT GENMASK(31, 28) +#define HAL_ENCRYPT_TYPE_MAX 12 struct hal_wbm_release_ring { struct ath12k_buffer_addr buf_addr_info; @@ -2048,6 +2081,19 @@ struct hal_wbm_release_ring { * fw with fw_reason2. * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3: Remove command initiated by * fw with fw_reason3. + * @HAL_WBM_TQM_REL_REASON_CMD_DISABLE_QUEUE: Remove command initiated by + * fw with disable queue. + * @HAL_WBM_TQM_REL_REASON_CMD_TILL_NONMATCHING: Remove command initiated by + * fw to remove all mpdu until 1st non-match. + * @HAL_WBM_TQM_REL_REASON_DROP_THRESHOLD: Dropped due to drop threshold + * criteria + * @HAL_WBM_TQM_REL_REASON_DROP_LINK_DESC_UNAVAIL: Dropped due to link desc + * not available + * @HAL_WBM_TQM_REL_REASON_DROP_OR_INVALID_MSDU: Dropped due drop bit set or + * null flow + * @HAL_WBM_TQM_REL_REASON_MULTICAST_DROP: Dropped due mcast drop set for VDEV + * @HAL_WBM_TQM_REL_REASON_VDEV_MISMATCH_DROP: Dropped due to being set with + * 'TCL_drop_reason' */ enum hal_wbm_tqm_rel_reason { HAL_WBM_TQM_REL_REASON_FRAME_ACKED, @@ -2058,6 +2104,13 @@ enum hal_wbm_tqm_rel_reason { HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1, HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2, HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3, + HAL_WBM_TQM_REL_REASON_CMD_DISABLE_QUEUE, + HAL_WBM_TQM_REL_REASON_CMD_TILL_NONMATCHING, + HAL_WBM_TQM_REL_REASON_DROP_THRESHOLD, + HAL_WBM_TQM_REL_REASON_DROP_LINK_DESC_UNAVAIL, + HAL_WBM_TQM_REL_REASON_DROP_OR_INVALID_MSDU, + HAL_WBM_TQM_REL_REASON_MULTICAST_DROP, + HAL_WBM_TQM_REL_REASON_VDEV_MISMATCH_DROP, }; struct hal_wbm_buffer_ring { @@ -2924,9 +2977,8 @@ struct hal_mon_buf_ring { #define HAL_MON_DEST_COOKIE_BUF_ID GENMASK(17, 0) -#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(15, 0) -#define HAL_MON_DEST_INFO0_FLUSH_DETECTED BIT(16) -#define HAL_MON_DEST_INFO0_END_OF_PPDU BIT(17) +#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(11, 0) +#define HAL_MON_DEST_INFO0_END_REASON GENMASK(17, 16) #define HAL_MON_DEST_INFO0_INITIATOR BIT(18) #define HAL_MON_DEST_INFO0_EMPTY_DESC BIT(19) #define HAL_MON_DEST_INFO0_RING_ID GENMASK(27, 20) @@ -2964,4 +3016,29 @@ struct hal_mon_dest_desc { * updated by SRNG. */ +#define HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_FLAG BIT(8) +#define HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_TYPE GENMASK(16, 15) +#define HAL_TX_MSDU_METADATA_INFO0_HOST_TX_DESC_POOL BIT(31) + +struct hal_tx_msdu_metadata { + __le32 info0; + __le32 rsvd0[6]; +} __packed; + +/* hal_tx_msdu_metadata + * valid_encrypt_type + * if set, encrypt type is valid + * encrypt_type + * 0 = NO_ENCRYPT, + * 1 = ENCRYPT, + * 2 ~ 3 - Reserved + * host_tx_desc_pool + * If set, Firmware allocates tx_descriptors + * in WAL_BUFFERID_TX_HOST_DATA_EXP,instead + * of WAL_BUFFERID_TX_TCL_DATA_EXP. + * Use cases: + * Any time firmware uses TQM-BYPASS for Data + * TID, firmware expect host to set this bit. + */ + #endif /* ATH12K_HAL_DESC_H */ diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c index f7c1aaa3b5d4..48aa48c48606 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.c +++ b/drivers/net/wireless/ath/ath12k/hal_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "debug.h" @@ -26,8 +26,8 @@ static int ath12k_hal_reo_cmd_queue_stats(struct hal_tlv_64_hdr *tlv, { struct hal_reo_get_queue_stats *desc; - tlv->tl = u32_encode_bits(HAL_REO_GET_QUEUE_STATS, HAL_TLV_HDR_TAG) | - u32_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN); + 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; memset_startat(desc, 0, queue_addr_lo); @@ -59,8 +59,8 @@ static int ath12k_hal_reo_cmd_flush_cache(struct ath12k_hal *hal, hal->current_blk_index = avail_slot; } - tlv->tl = u32_encode_bits(HAL_REO_FLUSH_CACHE, HAL_TLV_HDR_TAG) | - u32_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN); + 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; memset_startat(desc, 0, cache_addr_lo); @@ -97,8 +97,8 @@ static int ath12k_hal_reo_cmd_update_rx_queue(struct hal_tlv_64_hdr *tlv, { struct hal_reo_update_rx_queue *desc; - tlv->tl = u32_encode_bits(HAL_REO_UPDATE_RX_REO_QUEUE, HAL_TLV_HDR_TAG) | - u32_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN); + 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; memset_startat(desc, 0, queue_addr_lo); @@ -326,7 +326,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab, HAL_REO_DEST_RING_INFO0_PUSH_REASON); err_code = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_ERROR_CODE); - ab->soc_stats.reo_error[err_code]++; + ab->device_stats.reo_error[err_code]++; if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED && push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { @@ -381,7 +381,7 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc, val = le32_get_bits(wbm_desc->buf_addr_info.info1, BUFFER_ADDR_INFO1_RET_BUF_MGR); if (val != HAL_RX_BUF_RBM_SW3_BM) { - ab->soc_stats.invalid_rbm++; + ab->device_stats.invalid_rbm++; return -EINVAL; } @@ -393,7 +393,7 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc, val = le32_get_bits(wbm_cc_desc->info0, HAL_WBM_RELEASE_RX_CC_INFO0_RBM); if (val != HAL_RX_BUF_RBM_SW3_BM) { - ab->soc_stats.invalid_rbm++; + ab->device_stats.invalid_rbm++; return -EINVAL; } @@ -446,17 +446,97 @@ void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab, *cookie = le32_get_bits(buff_addr->info1, BUFFER_ADDR_INFO1_SW_COOKIE); } +void ath12k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, + u32 *sw_cookie, + struct ath12k_buffer_addr **pp_buf_addr, + u8 *rbm, u32 *msdu_cnt) +{ + struct hal_reo_entrance_ring *reo_ent_ring = + (struct hal_reo_entrance_ring *)rx_desc; + struct ath12k_buffer_addr *buf_addr_info; + struct rx_mpdu_desc *rx_mpdu_desc_info_details; + + rx_mpdu_desc_info_details = + (struct rx_mpdu_desc *)&reo_ent_ring->rx_mpdu_info; + + *msdu_cnt = le32_get_bits(rx_mpdu_desc_info_details->info0, + RX_MPDU_DESC_INFO0_MSDU_COUNT); + + buf_addr_info = (struct ath12k_buffer_addr *)&reo_ent_ring->buf_addr_info; + + *paddr = (((u64)le32_get_bits(buf_addr_info->info1, + BUFFER_ADDR_INFO1_ADDR)) << 32) | + le32_get_bits(buf_addr_info->info0, + BUFFER_ADDR_INFO0_ADDR); + + *sw_cookie = le32_get_bits(buf_addr_info->info1, + BUFFER_ADDR_INFO1_SW_COOKIE); + *rbm = le32_get_bits(buf_addr_info->info1, + BUFFER_ADDR_INFO1_RET_BUF_MGR); + + *pp_buf_addr = (void *)buf_addr_info; +} + +void ath12k_hal_rx_msdu_list_get(struct ath12k *ar, + struct hal_rx_msdu_link *link_desc, + struct hal_rx_msdu_list *msdu_list, + u16 *num_msdus) +{ + struct hal_rx_msdu_details *msdu_details = NULL; + struct rx_msdu_desc *msdu_desc_info = NULL; + u32 last = 0, first = 0; + u8 tmp = 0; + int i; + + last = u32_encode_bits(last, RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + first = u32_encode_bits(first, RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + msdu_details = &link_desc->msdu_link[0]; + + for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (!i && le32_get_bits(msdu_details[i].buf_addr_info.info0, + BUFFER_ADDR_INFO0_ADDR) == 0) + break; + if (le32_get_bits(msdu_details[i].buf_addr_info.info0, + BUFFER_ADDR_INFO0_ADDR) == 0) { + msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; + msdu_desc_info->info0 |= cpu_to_le32(last); + break; + } + msdu_desc_info = &msdu_details[i].rx_msdu_info; + + if (!i) + msdu_desc_info->info0 |= cpu_to_le32(first); + else if (i == (HAL_RX_NUM_MSDU_DESC - 1)) + msdu_desc_info->info0 |= cpu_to_le32(last); + msdu_list->msdu_info[i].msdu_flags = le32_to_cpu(msdu_desc_info->info0); + msdu_list->msdu_info[i].msdu_len = + HAL_RX_MSDU_PKT_LENGTH_GET(msdu_desc_info->info0); + msdu_list->sw_cookie[i] = + le32_get_bits(msdu_details[i].buf_addr_info.info1, + BUFFER_ADDR_INFO1_SW_COOKIE); + tmp = le32_get_bits(msdu_details[i].buf_addr_info.info1, + BUFFER_ADDR_INFO1_RET_BUF_MGR); + msdu_list->paddr[i] = + ((u64)(le32_get_bits(msdu_details[i].buf_addr_info.info1, + BUFFER_ADDR_INFO1_ADDR)) << 32) | + le32_get_bits(msdu_details[i].buf_addr_info.info0, + BUFFER_ADDR_INFO0_ADDR); + msdu_list->rbm[i] = tmp; + } + *num_msdus = i; +} + void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab, - struct hal_wbm_release_ring *dst_desc, - struct hal_wbm_release_ring *src_desc, + struct hal_wbm_release_ring *desc, + struct ath12k_buffer_addr *buf_addr_info, enum hal_wbm_rel_bm_act action) { - dst_desc->buf_addr_info = src_desc->buf_addr_info; - dst_desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW, - HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) | - le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) | - le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK, - HAL_WBM_RELEASE_INFO0_DESC_TYPE); + desc->buf_addr_info = *buf_addr_info; + desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW, + HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) | + le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) | + le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK, + HAL_WBM_RELEASE_INFO0_DESC_TYPE); } void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv, @@ -851,3 +931,20 @@ void ath12k_hal_reo_hw_setup(struct ath12k_base *ab, u32 ring_hash_map) ath12k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, ring_hash_map); } + +void ath12k_hal_reo_shared_qaddr_cache_clear(struct ath12k_base *ab) +{ + u32 val; + + lockdep_assert_held(&ab->base_lock); + val = ath12k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_ADDR(ab)); + + val |= u32_encode_bits(1, HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY); + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_ADDR(ab), val); + + val &= ~HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY; + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_ADDR(ab), val); +} diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 095216eabc01..a3ab588aae19 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HAL_RX_H @@ -19,12 +19,9 @@ struct hal_rx_wbm_rel_info { bool hw_cc_done; }; -#define HAL_INVALID_PEERID 0xffff +#define HAL_INVALID_PEERID 0x3fff #define VHT_SIG_SU_NSS_MASK 0x7 -#define HAL_RX_MAX_MCS 12 -#define HAL_RX_MAX_NSS 8 - #define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \ le32_get_bits((__val), GENMASK(7, 0)) @@ -71,6 +68,8 @@ enum hal_rx_preamble { HAL_RX_PREAMBLE_11N, HAL_RX_PREAMBLE_11AC, HAL_RX_PREAMBLE_11AX, + HAL_RX_PREAMBLE_11BA, + HAL_RX_PREAMBLE_11BE, HAL_RX_PREAMBLE_MAX, }; @@ -108,9 +107,13 @@ enum hal_rx_mon_status { HAL_RX_MON_STATUS_PPDU_NOT_DONE, HAL_RX_MON_STATUS_PPDU_DONE, HAL_RX_MON_STATUS_BUF_DONE, + HAL_RX_MON_STATUS_BUF_ADDR, + HAL_RX_MON_STATUS_MPDU_START, + HAL_RX_MON_STATUS_MPDU_END, + HAL_RX_MON_STATUS_MSDU_END, }; -#define HAL_RX_MAX_MPDU 256 +#define HAL_RX_MAX_MPDU 1024 #define HAL_RX_NUM_WORDS_PER_PPDU_BITMAP (HAL_RX_MAX_MPDU >> 5) struct hal_rx_user_status { @@ -143,10 +146,43 @@ struct hal_rx_user_status { u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; u32 mpdu_ok_byte_count; u32 mpdu_err_byte_count; + bool ampdu_present; + u16 ampdu_id; }; #define HAL_MAX_UL_MU_USERS 37 +struct hal_rx_u_sig_info { + bool ul_dl; + u8 bw; + u8 ppdu_type_comp_mode; + u8 eht_sig_mcs; + u8 num_eht_sig_sym; + struct ieee80211_radiotap_eht_usig usig; +}; + +#define HAL_RX_MON_MAX_AGGR_SIZE 128 + +struct hal_rx_tlv_aggr_info { + bool in_progress; + u16 cur_len; + u16 tlv_tag; + u8 buf[HAL_RX_MON_MAX_AGGR_SIZE]; +}; + +struct hal_rx_radiotap_eht { + __le32 known; + __le32 data[9]; +}; + +#define EHT_MAX_USER_INFO 4 + +struct hal_rx_eht_info { + u8 num_user_info; + struct hal_rx_radiotap_eht eht; + u32 user_info[EHT_MAX_USER_INFO]; +}; + struct hal_rx_mon_ppdu_info { u32 ppdu_id; u32 last_ppdu_id; @@ -156,6 +192,7 @@ struct hal_rx_mon_ppdu_info { u32 preamble_type; u32 mpdu_len; u16 chan_num; + u16 freq; u16 tcp_msdu_count; u16 tcp_ack_msdu_count; u16 udp_msdu_count; @@ -226,27 +263,38 @@ struct hal_rx_mon_ppdu_info { u8 addr4[ETH_ALEN]; struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS]; u8 userid; - u16 ampdu_id[HAL_MAX_UL_MU_USERS]; bool first_msdu_in_mpdu; bool is_ampdu; u8 medium_prot_type; + bool ppdu_continuation; + bool eht_usig; + struct hal_rx_u_sig_info u_sig_info; + bool is_eht; + struct hal_rx_eht_info eht_info; + struct hal_rx_tlv_aggr_info tlv_aggr; }; -#define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) +#define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) +#define HAL_RX_PPDU_START_INFO1_CHAN_NUM GENMASK(15, 0) +#define HAL_RX_PPDU_START_INFO1_CHAN_FREQ GENMASK(31, 16) struct hal_rx_ppdu_start { __le32 info0; - __le32 chan_num; - __le32 ppdu_start_ts; + __le32 info1; + __le32 ppdu_start_ts_31_0; + __le32 ppdu_start_ts_63_32; + __le32 rsvd[2]; } __packed; -#define HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR GENMASK(25, 16) +#define HAL_RX_PPDU_END_USER_STATS_INFO0_PEER_ID GENMASK(13, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO0_DEVICE_ID GENMASK(15, 14) +#define HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR GENMASK(26, 16) -#define HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK GENMASK(8, 0) -#define HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID BIT(9) -#define HAL_RX_PPDU_END_USER_STATS_INFO1_QOS_CTRL_VALID BIT(10) -#define HAL_RX_PPDU_END_USER_STATS_INFO1_HT_CTRL_VALID BIT(11) -#define HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE GENMASK(23, 20) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK GENMASK(10, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID BIT(11) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_QOS_CTRL_VALID BIT(12) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_HT_CTRL_VALID BIT(13) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE GENMASK(24, 21) #define HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX GENMASK(15, 0) #define HAL_RX_PPDU_END_USER_STATS_INFO2_FRAME_CTRL GENMASK(31, 16) @@ -262,8 +310,8 @@ struct hal_rx_ppdu_start { #define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP GENMASK(15, 0) #define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_EOSP_BITMAP GENMASK(31, 16) -#define HAL_RX_PPDU_END_USER_STATS_MPDU_DELIM_OK_BYTE_COUNT GENMASK(24, 0) -#define HAL_RX_PPDU_END_USER_STATS_MPDU_DELIM_ERR_BYTE_COUNT GENMASK(24, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO7_MPDU_OK_BYTE_COUNT GENMASK(24, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO8_MPDU_ERR_BYTE_COUNT GENMASK(24, 0) struct hal_rx_ppdu_end_user_stats { __le32 rsvd0[2]; @@ -278,9 +326,9 @@ struct hal_rx_ppdu_end_user_stats { __le32 usr_resp_ref; __le32 info6; __le32 rsvd3[4]; - __le32 mpdu_ok_cnt; + __le32 info7; __le32 rsvd4; - __le32 mpdu_err_cnt; + __le32 info8; __le32 rsvd5[2]; __le32 usr_resp_ref_ext; __le32 rsvd6; @@ -294,6 +342,7 @@ struct hal_rx_ppdu_end_user_stats_ext { __le32 info4; __le32 info5; __le32 info6; + __le32 rsvd; } __packed; #define HAL_RX_HT_SIG_INFO_INFO0_MCS GENMASK(6, 0) @@ -390,11 +439,9 @@ struct hal_rx_he_sig_a_su_info { #define HAL_RX_HE_SIG_A_MU_DL_INFO0_DOPPLER_INDICATION BIT(25) #define HAL_RX_HE_SIG_A_MU_DL_INFO1_TXOP_DURATION GENMASK(6, 0) -#define HAL_RX_HE_SIG_A_MU_DL_INFO1_CODING BIT(7) #define HAL_RX_HE_SIG_A_MU_DL_INFO1_NUM_LTF_SYMB GENMASK(10, 8) #define HAL_RX_HE_SIG_A_MU_DL_INFO1_LDPC_EXTRA BIT(11) #define HAL_RX_HE_SIG_A_MU_DL_INFO1_STBC BIT(12) -#define HAL_RX_HE_SIG_A_MU_DL_INFO1_TXBF BIT(10) #define HAL_RX_HE_SIG_A_MU_DL_INFO1_PKT_EXT_FACTOR GENMASK(14, 13) #define HAL_RX_HE_SIG_A_MU_DL_INFO1_PKT_EXT_PE_DISAM BIT(15) @@ -420,7 +467,7 @@ struct hal_rx_he_sig_b2_mu_info { #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_ID GENMASK(10, 0) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS GENMASK(13, 11) -#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(19) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(14) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS GENMASK(18, 15) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_DCM BIT(19) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING BIT(20) @@ -436,30 +483,47 @@ enum hal_rx_ul_reception_type { HAL_RECEPTION_TYPE_FRAMELESS }; -#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RSSI_COMB GENMASK(15, 8) -#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_RSVD1_RECEPTION GENMASK(3, 0) +#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RECEPTION GENMASK(3, 0) +#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RX_BW GENMASK(7, 5) +#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB GENMASK(15, 8) struct hal_rx_phyrx_rssi_legacy_info { - __le32 rsvd[35]; __le32 info0; + __le32 rsvd0[39]; + __le32 info1; + __le32 rsvd1; } __packed; -#define HAL_RX_MPDU_START_INFO0_PPDU_ID GENMASK(31, 16) -#define HAL_RX_MPDU_START_INFO1_PEERID GENMASK(31, 16) -#define HAL_RX_MPDU_START_INFO2_MPDU_LEN GENMASK(13, 0) +#define HAL_RX_MPDU_START_INFO0_PPDU_ID GENMASK(31, 16) +#define HAL_RX_MPDU_START_INFO1_PEERID GENMASK(29, 16) +#define HAL_RX_MPDU_START_INFO1_DEVICE_ID GENMASK(31, 30) +#define HAL_RX_MPDU_START_INFO2_MPDU_LEN GENMASK(13, 0) struct hal_rx_mpdu_start { + __le32 rsvd0[9]; __le32 info0; __le32 info1; - __le32 rsvd1[11]; + __le32 rsvd1[2]; + __le32 info2; + __le32 rsvd2[16]; +} __packed; + +struct hal_rx_msdu_end { + __le32 info0; + __le32 rsvd0[9]; + __le16 info00; + __le16 info01; + __le32 rsvd00[8]; + __le32 info1; + __le32 rsvd1[10]; __le32 info2; - __le32 rsvd2[9]; + __le32 rsvd2; } __packed; #define HAL_RX_PPDU_END_DURATION GENMASK(23, 0) struct hal_rx_ppdu_end_duration { __le32 rsvd0[9]; __le32 info0; - __le32 rsvd1[4]; + __le32 rsvd1[18]; } __packed; struct hal_rx_rxpcu_classification_overview { @@ -474,6 +538,7 @@ struct hal_rx_msdu_desc_info { #define HAL_RX_NUM_MSDU_DESC 6 struct hal_rx_msdu_list { struct hal_rx_msdu_desc_info msdu_info[HAL_RX_NUM_MSDU_DESC]; + u64 paddr[HAL_RX_NUM_MSDU_DESC]; u32 sw_cookie[HAL_RX_NUM_MSDU_DESC]; u8 rbm[HAL_RX_NUM_MSDU_DESC]; }; @@ -630,6 +695,395 @@ struct hal_rx_resp_req_info { #define HAL_RX_MPDU_ERR_MPDU_LEN BIT(6) #define HAL_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) +#define HAL_RX_PHY_CMN_USER_INFO0_GI GENMASK(17, 16) + +struct hal_phyrx_common_user_info { + __le32 rsvd[2]; + __le32 info0; + __le32 rsvd1; +} __packed; + +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS GENMASK(10, 7) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED BIT(11) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD GENMASK(13, 12) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC GENMASK(17, 14) + +struct hal_eht_sig_ndp_cmn_eb { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM BIT(9) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD GENMASK(16, 13) + +struct hal_eht_sig_usig_overflow { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS GENMASK(14, 11) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_VALIDATE BIT(15) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS GENMASK(19, 16) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED BIT(20) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING BIT(21) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CRC GENMASK(25, 22) + +struct hal_eht_sig_non_mu_mimo { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS GENMASK(14, 11) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING BIT(15) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING GENMASK(22, 16) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CRC GENMASK(26, 23) + +struct hal_eht_sig_mu_mimo { + __le32 info0; +} __packed; + +union hal_eht_sig_user_field { + struct hal_eht_sig_mu_mimo mu_mimo; + struct hal_eht_sig_non_mu_mimo n_mu_mimo; +}; + +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_LDPC_EXTA_SYM BIT(9) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISREGARD GENMASK(16, 13) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS GENMASK(19, 17) + +struct hal_eht_sig_non_ofdma_cmn_eb { + __le32 info0; + union hal_eht_sig_user_field user_field; +} __packed; + +#define HAL_RX_EHT_SIG_OFDMA_EB1_SPATIAL_REUSE GENMASK_ULL(3, 0) +#define HAL_RX_EHT_SIG_OFDMA_EB1_GI_LTF GENMASK_ULL(5, 4) +#define HAL_RX_EHT_SIG_OFDMA_EB1_NUM_LFT_SYM GENMASK_ULL(8, 6) +#define HAL_RX_EHT_SIG_OFDMA_EB1_LDPC_EXTRA_SYM BIT(9) +#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_FEC_PAD_FACTOR GENMASK_ULL(11, 10) +#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_OFDMA_EB1_DISREGARD GENMASK_ULL(16, 13) +#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1 GENMASK_ULL(25, 17) +#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2 GENMASK_ULL(34, 26) +#define HAL_RX_EHT_SIG_OFDMA_EB1_CRC GENMASK_ULL(30, 27) + +struct hal_eht_sig_ofdma_cmn_eb1 { + __le64 info0; +} __packed; + +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1 GENMASK_ULL(8, 0) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2 GENMASK_ULL(17, 9) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3 GENMASK_ULL(26, 18) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45) +#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54) + +struct hal_eht_sig_ofdma_cmn_eb2 { + __le64 info0; +} __packed; + +struct hal_eht_sig_ofdma_cmn_eb { + struct hal_eht_sig_ofdma_cmn_eb1 eb1; + struct hal_eht_sig_ofdma_cmn_eb2 eb2; + union hal_eht_sig_user_field user_field; +} __packed; + +enum hal_eht_bw { + HAL_EHT_BW_20, + HAL_EHT_BW_40, + HAL_EHT_BW_80, + HAL_EHT_BW_160, + HAL_EHT_BW_320_1, + HAL_EHT_BW_320_2, +}; + +#define HAL_RX_USIG_CMN_INFO0_PHY_VERSION GENMASK(2, 0) +#define HAL_RX_USIG_CMN_INFO0_BW GENMASK(5, 3) +#define HAL_RX_USIG_CMN_INFO0_UL_DL BIT(6) +#define HAL_RX_USIG_CMN_INFO0_BSS_COLOR GENMASK(12, 7) +#define HAL_RX_USIG_CMN_INFO0_TXOP GENMASK(19, 13) +#define HAL_RX_USIG_CMN_INFO0_DISREGARD GENMASK(25, 20) +#define HAL_RX_USIG_CMN_INFO0_VALIDATE BIT(26) + +struct hal_mon_usig_cmn { + __le32 info0; +} __packed; + +#define HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0) +#define HAL_RX_USIG_TB_INFO0_VALIDATE BIT(2) +#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1 GENMASK(6, 3) +#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2 GENMASK(10, 7) +#define HAL_RX_USIG_TB_INFO0_DISREGARD_1 GENMASK(15, 11) +#define HAL_RX_USIG_TB_INFO0_CRC GENMASK(19, 16) +#define HAL_RX_USIG_TB_INFO0_TAIL GENMASK(25, 20) +#define HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS BIT(31) + +struct hal_mon_usig_tb { + __le32 info0; +} __packed; + +#define HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0) +#define HAL_RX_USIG_MU_INFO0_VALIDATE_1 BIT(2) +#define HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO GENMASK(7, 3) +#define HAL_RX_USIG_MU_INFO0_VALIDATE_2 BIT(8) +#define HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS GENMASK(10, 9) +#define HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM GENMASK(15, 11) +#define HAL_RX_USIG_MU_INFO0_CRC GENMASK(20, 16) +#define HAL_RX_USIG_MU_INFO0_TAIL GENMASK(26, 21) +#define HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS BIT(31) + +struct hal_mon_usig_mu { + __le32 info0; +} __packed; + +union hal_mon_usig_non_cmn { + struct hal_mon_usig_tb tb; + struct hal_mon_usig_mu mu; +}; + +struct hal_mon_usig_hdr { + struct hal_mon_usig_cmn cmn; + union hal_mon_usig_non_cmn non_cmn; +} __packed; + +#define HAL_RX_USR_INFO0_PHY_PPDU_ID GENMASK(15, 0) +#define HAL_RX_USR_INFO0_USR_RSSI GENMASK(23, 16) +#define HAL_RX_USR_INFO0_PKT_TYPE GENMASK(27, 24) +#define HAL_RX_USR_INFO0_STBC BIT(28) +#define HAL_RX_USR_INFO0_RECEPTION_TYPE GENMASK(31, 29) + +#define HAL_RX_USR_INFO1_MCS GENMASK(3, 0) +#define HAL_RX_USR_INFO1_SGI GENMASK(5, 4) +#define HAL_RX_USR_INFO1_HE_RANGING_NDP BIT(6) +#define HAL_RX_USR_INFO1_MIMO_SS_BITMAP GENMASK(15, 8) +#define HAL_RX_USR_INFO1_RX_BW GENMASK(18, 16) +#define HAL_RX_USR_INFO1_DL_OFMDA_USR_IDX GENMASK(31, 24) + +#define HAL_RX_USR_INFO2_DL_OFDMA_CONTENT_CHAN BIT(0) +#define HAL_RX_USR_INFO2_NSS GENMASK(10, 8) +#define HAL_RX_USR_INFO2_STREAM_OFFSET GENMASK(13, 11) +#define HAL_RX_USR_INFO2_STA_DCM BIT(14) +#define HAL_RX_USR_INFO2_LDPC BIT(15) +#define HAL_RX_USR_INFO2_RU_TYPE_80_0 GENMASK(19, 16) +#define HAL_RX_USR_INFO2_RU_TYPE_80_1 GENMASK(23, 20) +#define HAL_RX_USR_INFO2_RU_TYPE_80_2 GENMASK(27, 24) +#define HAL_RX_USR_INFO2_RU_TYPE_80_3 GENMASK(31, 28) + +#define HAL_RX_USR_INFO3_RU_START_IDX_80_0 GENMASK(5, 0) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_1 GENMASK(13, 8) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_2 GENMASK(21, 16) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_3 GENMASK(29, 24) + +struct hal_receive_user_info { + __le32 info0; + __le32 info1; + __le32 info2; + __le32 info3; + __le32 user_fd_rssi_seg0; + __le32 user_fd_rssi_seg1; + __le32 user_fd_rssi_seg2; + __le32 user_fd_rssi_seg3; +} __packed; + +enum hal_mon_reception_type { + HAL_RECEPTION_TYPE_SU, + HAL_RECEPTION_TYPE_DL_MU_MIMO, + HAL_RECEPTION_TYPE_DL_MU_OFMA, + HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO, + HAL_RECEPTION_TYPE_UL_MU_MIMO, + HAL_RECEPTION_TYPE_UL_MU_OFDMA, + HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO, +}; + +/* Different allowed RU in 11BE */ +#define HAL_EHT_RU_26 0ULL +#define HAL_EHT_RU_52 1ULL +#define HAL_EHT_RU_78 2ULL +#define HAL_EHT_RU_106 3ULL +#define HAL_EHT_RU_132 4ULL +#define HAL_EHT_RU_242 5ULL +#define HAL_EHT_RU_484 6ULL +#define HAL_EHT_RU_726 7ULL +#define HAL_EHT_RU_996 8ULL +#define HAL_EHT_RU_996x2 9ULL +#define HAL_EHT_RU_996x3 10ULL +#define HAL_EHT_RU_996x4 11ULL +#define HAL_EHT_RU_NONE 15ULL +#define HAL_EHT_RU_INVALID 31ULL +/* MRUs spanning above 80Mhz + * HAL_EHT_RU_996_484 = HAL_EHT_RU_484 + HAL_EHT_RU_996 + 4 (reserved) + */ +#define HAL_EHT_RU_996_484 18ULL +#define HAL_EHT_RU_996x2_484 28ULL +#define HAL_EHT_RU_996x3_484 40ULL +#define HAL_EHT_RU_996_484_242 23ULL + +#define NUM_RU_BITS_PER80 16 +#define NUM_RU_BITS_PER20 4 + +/* Different per_80Mhz band in 320Mhz bandwidth */ +#define HAL_80_0 0 +#define HAL_80_1 1 +#define HAL_80_2 2 +#define HAL_80_3 3 + +#define HAL_RU_80MHZ(num_band) ((num_band) * NUM_RU_BITS_PER80) +#define HAL_RU_20MHZ(idx_per_80) ((idx_per_80) * NUM_RU_BITS_PER20) + +#define HAL_RU_SHIFT(num_band, idx_per_80) \ + (HAL_RU_80MHZ(num_band) + HAL_RU_20MHZ(idx_per_80)) + +#define HAL_RU(ru, num_band, idx_per_80) \ + ((u64)(ru) << HAL_RU_SHIFT(num_band, idx_per_80)) + +/* MRU-996+484 */ +#define HAL_EHT_RU_996_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_2 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1)) +#define HAL_EHT_RU_996_484_3 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_4 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0)) +#define HAL_EHT_RU_996_484_5 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0)) +#define HAL_EHT_RU_996_484_6 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996_484_7 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +/* MRU-996x2+484 */ +#define HAL_EHT_RU_996x2_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_2 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_3 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_4 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1)) +#define HAL_EHT_RU_996x2_484_5 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_6 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_7 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_8 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_9 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_10 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996x2_484_11 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +/* MRU-996x3+484 */ +#define HAL_EHT_RU_996x3_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_2 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_3 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_4 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_5 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_6 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996x3_484_7 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +#define HAL_RU_PER80(ru_per80, num_80mhz, ru_idx_per80mhz) \ + (HAL_RU(ru_per80, num_80mhz, ru_idx_per80mhz)) + +#define RU_INVALID 0 +#define RU_26 1 +#define RU_52 2 +#define RU_106 4 +#define RU_242 9 +#define RU_484 18 +#define RU_996 37 +#define RU_2X996 74 +#define RU_3X996 111 +#define RU_4X996 148 +#define RU_52_26 (RU_52 + RU_26) +#define RU_106_26 (RU_106 + RU_26) +#define RU_484_242 (RU_484 + RU_242) +#define RU_996_484 (RU_996 + RU_484) +#define RU_996_484_242 (RU_996 + RU_484_242) +#define RU_2X996_484 (RU_2X996 + RU_484) +#define RU_3X996_484 (RU_3X996 + RU_484) + +enum ath12k_eht_ru_size { + ATH12K_EHT_RU_26, + ATH12K_EHT_RU_52, + ATH12K_EHT_RU_106, + ATH12K_EHT_RU_242, + ATH12K_EHT_RU_484, + ATH12K_EHT_RU_996, + ATH12K_EHT_RU_996x2, + ATH12K_EHT_RU_996x4, + ATH12K_EHT_RU_52_26, + ATH12K_EHT_RU_106_26, + ATH12K_EHT_RU_484_242, + ATH12K_EHT_RU_996_484, + ATH12K_EHT_RU_996_484_242, + ATH12K_EHT_RU_996x2_484, + ATH12K_EHT_RU_996x3, + ATH12K_EHT_RU_996x3_484, + + /* Keep last */ + ATH12K_EHT_RU_INVALID, +}; + +#define HAL_RX_RU_ALLOC_TYPE_MAX ATH12K_EHT_RU_INVALID + static inline enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) { @@ -651,6 +1105,9 @@ enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) case RU_996: ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; break; + case RU_2X996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; case RU_26: fallthrough; default: @@ -685,8 +1142,8 @@ void ath12k_hal_rx_msdu_link_info_get(struct hal_rx_msdu_link *link, u32 *num_ms u32 *msdu_cookies, enum hal_rx_buf_return_buf_manager *rbm); void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab, - struct hal_wbm_release_ring *dst_desc, - struct hal_wbm_release_ring *src_desc, + struct hal_wbm_release_ring *desc, + struct ath12k_buffer_addr *buf_addr_info, enum hal_wbm_rel_bm_act action); void ath12k_hal_rx_buf_addr_info_set(struct ath12k_buffer_addr *binfo, dma_addr_t paddr, u32 cookie, u8 manager); @@ -701,5 +1158,12 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc, void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab, struct ath12k_buffer_addr *buff_addr, dma_addr_t *paddr, u32 *cookie); +void ath12k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, u32 *sw_cookie, + struct ath12k_buffer_addr **pp_buf_addr, + u8 *rbm, u32 *msdu_cnt); +void ath12k_hal_rx_msdu_list_get(struct ath12k *ar, + struct hal_rx_msdu_link *link_desc, + struct hal_rx_msdu_list *msdu_list, + u16 *num_msdus); #endif diff --git a/drivers/net/wireless/ath/ath12k/hal_tx.h b/drivers/net/wireless/ath/ath12k/hal_tx.h index 7c837094a6f7..eb065a79f6c6 100644 --- a/drivers/net/wireless/ath/ath12k/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/hal_tx.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. + * All rights reserved. */ #ifndef ATH12K_HAL_TX_H @@ -57,13 +58,18 @@ struct hal_tx_info { struct hal_tx_status { enum hal_wbm_rel_src_module buf_rel_source; enum hal_wbm_tqm_rel_reason status; - u8 ack_rssi; + s8 ack_rssi; u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ u32 ppdu_id; u8 try_cnt; u8 tid; u16 peer_id; - u32 rate_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum ath12k_supported_bw bw; + u8 mcs; + u16 tones; + u8 ofdma; }; #define HAL_TX_PHY_DESC_INFO0_BF_TYPE GENMASK(17, 16) diff --git a/drivers/net/wireless/ath/ath12k/hif.h b/drivers/net/wireless/ath/ath12k/hif.h index c653ca1f59b2..e8840fab6061 100644 --- a/drivers/net/wireless/ath/ath12k/hif.h +++ b/drivers/net/wireless/ath/ath12k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HIF_H @@ -17,7 +17,7 @@ struct ath12k_hif_ops { int (*start)(struct ath12k_base *ab); void (*stop)(struct ath12k_base *ab); int (*power_up)(struct ath12k_base *ab); - void (*power_down)(struct ath12k_base *ab); + void (*power_down)(struct ath12k_base *ab, bool is_suspend); int (*suspend)(struct ath12k_base *ab); int (*resume)(struct ath12k_base *ab); int (*map_service_to_pipe)(struct ath12k_base *ab, u16 service_id, @@ -30,6 +30,8 @@ struct ath12k_hif_ops { void (*ce_irq_enable)(struct ath12k_base *ab); void (*ce_irq_disable)(struct ath12k_base *ab); void (*get_ce_msi_idx)(struct ath12k_base *ab, u32 ce_id, u32 *msi_idx); + int (*panic_handler)(struct ath12k_base *ab); + void (*coredump_download)(struct ath12k_base *ab); }; static inline int ath12k_hif_map_service_to_pipe(struct ath12k_base *ab, u16 service_id, @@ -133,12 +135,31 @@ static inline void ath12k_hif_write32(struct ath12k_base *ab, u32 address, static inline int ath12k_hif_power_up(struct ath12k_base *ab) { + if (!ab->hif.ops->power_up) + return -EOPNOTSUPP; + return ab->hif.ops->power_up(ab); } -static inline void ath12k_hif_power_down(struct ath12k_base *ab) +static inline void ath12k_hif_power_down(struct ath12k_base *ab, bool is_suspend) +{ + if (!ab->hif.ops->power_down) + return; + + ab->hif.ops->power_down(ab, is_suspend); +} + +static inline int ath12k_hif_panic_handler(struct ath12k_base *ab) { - ab->hif.ops->power_down(ab); + if (!ab->hif.ops->panic_handler) + return NOTIFY_DONE; + + return ab->hif.ops->panic_handler(ab); } +static inline void ath12k_hif_coredump_download(struct ath12k_base *ab) +{ + if (ab->hif.ops->coredump_download) + ab->hif.ops->coredump_download(ab); +} #endif /* ATH12K_HIF_H */ diff --git a/drivers/net/wireless/ath/ath12k/htc.c b/drivers/net/wireless/ath/ath12k/htc.c index 23f7428abd95..d13616bf07f4 100644 --- a/drivers/net/wireless/ath/ath12k/htc.c +++ b/drivers/net/wireless/ath/ath12k/htc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -244,6 +244,11 @@ static void ath12k_htc_suspend_complete(struct ath12k_base *ab, bool ack) complete(&ab->htc_suspend); } +static void ath12k_htc_wakeup_from_suspend(struct ath12k_base *ab) +{ + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot wakeup from suspend is received\n"); +} + void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, struct sk_buff *skb) { @@ -349,6 +354,7 @@ void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, ath12k_htc_suspend_complete(ab, false); break; case ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID: + ath12k_htc_wakeup_from_suspend(ab); break; default: ath12k_warn(ab, "ignoring unsolicited htc ep0 event %u\n", @@ -358,7 +364,7 @@ void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, goto out; } - ath12k_dbg(ab, ATH12K_DBG_HTC, "htc rx completion ep %d skb %pK\n", + ath12k_dbg(ab, ATH12K_DBG_HTC, "htc rx completion ep %d skb %p\n", eid, skb); ep->ep_ops.ep_rx_complete(ab, skb); diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index 0b17dfd47856..8254dc10b53b 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/types.h> @@ -15,6 +15,10 @@ #include "mhi.h" #include "dp_rx.h" +static const guid_t wcn7850_uuid = GUID_INIT(0xf634f534, 0x6147, 0x11ec, + 0x90, 0xd6, 0x02, 0x42, + 0xac, 0x12, 0x00, 0x03); + static u8 ath12k_hw_qcn9274_mac_from_pdev_id(int pdev_idx) { return pdev_idx; @@ -114,6 +118,10 @@ static const struct ath12k_hw_ops wcn7850_ops = { #define ATH12K_TX_MON_RING_MASK_0 0x1 #define ATH12K_TX_MON_RING_MASK_1 0x2 +#define ATH12K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH12K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH12K_RX_MON_STATUS_RING_MASK_2 0x4 + /* Target firmware's Copy Engine configuration. */ static const struct ce_pipe_config ath12k_target_ce_config_wlan_qcn9274[] = { /* CE0: host->target HTC control and raw streams */ @@ -531,6 +539,217 @@ static const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_wcn7850 }, }; +static const struct ce_pipe_config ath12k_target_ce_config_wlan_ipq5332[] = { + /* host->target HTC control and raw streams */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* target->host HTT */ + { + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* target->host WMI + HTC control */ + { + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* host->target WMI */ + { + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* host->target HTT */ + { + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + /* Target -> host PKTLOG */ + { + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* Reserved for target autonomous HIF_memcpy */ + { + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(16384), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE7 Reserved for CV Prefetch */ + { + .pipenum = __cpu_to_le32(7), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE8 Reserved for target generic HIF memcpy */ + { + .pipenum = __cpu_to_le32(8), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(16384), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE9 WMI logging/CFR/Spectral/Radar/ */ + { + .pipenum = __cpu_to_le32(9), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* Unused TBD */ + { + .pipenum = __cpu_to_le32(10), + .pipedir = __cpu_to_le32(PIPEDIR_NONE), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, + /* Unused TBD */ + { + .pipenum = __cpu_to_le32(11), + .pipedir = __cpu_to_le32(PIPEDIR_NONE), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, +}; + +static const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_ipq5332[] = { + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(0), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(0), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(4), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_PKT_LOG), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(5), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL_DIAG), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(9), + }, + /* (Additions here) */ + + { /* must be last */ + __cpu_to_le32(0), + __cpu_to_le32(0), + __cpu_to_le32(0), + }, +}; + static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { .tx = { ATH12K_TX_RING_MASK_0, @@ -539,7 +758,8 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { ATH12K_TX_RING_MASK_3, }, .rx_mon_dest = { - 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, ATH12K_RX_MON_RING_MASK_0, ATH12K_RX_MON_RING_MASK_1, ATH12K_RX_MON_RING_MASK_2, @@ -568,6 +788,45 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { ATH12K_HOST2RXDMA_RING_MASK_0, }, .tx_mon_dest = { + 0, 0, 0, + }, +}; + +static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_ipq5332 = { + .tx = { + ATH12K_TX_RING_MASK_0, + ATH12K_TX_RING_MASK_1, + ATH12K_TX_RING_MASK_2, + ATH12K_TX_RING_MASK_3, + }, + .rx_mon_dest = { + 0, 0, 0, 0, 0, 0, 0, 0, + ATH12K_RX_MON_RING_MASK_0, + }, + .rx = { + 0, 0, 0, 0, + ATH12K_RX_RING_MASK_0, + ATH12K_RX_RING_MASK_1, + ATH12K_RX_RING_MASK_2, + ATH12K_RX_RING_MASK_3, + }, + .rx_err = { + 0, 0, 0, + ATH12K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + 0, 0, 0, + ATH12K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + 0, 0, 0, + ATH12K_REO_STATUS_RING_MASK_0, + }, + .host2rxdma = { + 0, 0, 0, + ATH12K_HOST2RXDMA_RING_MASK_0, + }, + .tx_mon_dest = { ATH12K_TX_MON_RING_MASK_0, ATH12K_TX_MON_RING_MASK_1, }, @@ -576,11 +835,17 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn7850 = { .tx = { ATH12K_TX_RING_MASK_0, + ATH12K_TX_RING_MASK_1, ATH12K_TX_RING_MASK_2, - ATH12K_TX_RING_MASK_4, }, .rx_mon_dest = { }, + .rx_mon_status = { + 0, 0, 0, 0, + ATH12K_RX_MON_STATUS_RING_MASK_0, + ATH12K_RX_MON_STATUS_RING_MASK_1, + ATH12K_RX_MON_STATUS_RING_MASK_2, + }, .rx = { 0, 0, 0, ATH12K_RX_RING_MASK_0, @@ -615,6 +880,9 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = { .hal_tcl1_ring_msi1_base_msb = 0x0000094c, .hal_tcl1_ring_msi1_data = 0x00000950, .hal_tcl_ring_base_lsb = 0x00000b58, + .hal_tcl1_ring_base_lsb = 0x00000900, + .hal_tcl1_ring_base_msb = 0x00000904, + .hal_tcl2_ring_base_lsb = 0x00000978, /* TCL STATUS ring address */ .hal_tcl_status_ring_base_lsb = 0x00000d38, @@ -677,6 +945,14 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = { /* REO status ring address */ .hal_reo_status_ring_base = 0x00000a84, + + /* 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 = 0x1e38338, }; static const struct ath12k_hw_regs qcn9274_v2_regs = { @@ -691,6 +967,9 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { .hal_tcl1_ring_msi1_base_msb = 0x0000094c, .hal_tcl1_ring_msi1_data = 0x00000950, .hal_tcl_ring_base_lsb = 0x00000b58, + .hal_tcl1_ring_base_lsb = 0x00000900, + .hal_tcl1_ring_base_msb = 0x00000904, + .hal_tcl2_ring_base_lsb = 0x00000978, /* TCL STATUS ring address */ .hal_tcl_status_ring_base_lsb = 0x00000d38, @@ -730,6 +1009,71 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { .hal_reo1_sw_cookie_cfg1 = 0x00000070, .hal_reo1_qdesc_lut_base0 = 0x00000074, .hal_reo1_qdesc_lut_base1 = 0x00000078, + .hal_reo1_qdesc_addr = 0x0000007c, + .hal_reo1_qdesc_max_peerid = 0x00000088, + .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 = 0x00000B28, + .hal_reo1_aging_thres_ix1 = 0x00000B2C, + .hal_reo1_aging_thres_ix2 = 0x00000B30, + .hal_reo1_aging_thres_ix3 = 0x00000B34, + + /* 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 = 0x1e38338, +}; + +static const struct ath12k_hw_regs ipq5332_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_id = 0x00000918, + .hal_tcl1_ring_misc = 0x00000920, + .hal_tcl1_ring_tp_addr_lsb = 0x0000092c, + .hal_tcl1_ring_tp_addr_msb = 0x00000930, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000940, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x00000944, + .hal_tcl1_ring_msi1_base_lsb = 0x00000958, + .hal_tcl1_ring_msi1_base_msb = 0x0000095c, + .hal_tcl1_ring_base_lsb = 0x00000910, + .hal_tcl1_ring_base_msb = 0x00000914, + .hal_tcl1_ring_msi1_data = 0x00000960, + .hal_tcl2_ring_base_lsb = 0x00000988, + .hal_tcl_ring_base_lsb = 0x00000b68, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000d48, + + /* REO DEST ring address */ + .hal_reo2_ring_base = 0x00000578, + .hal_reo1_misc_ctrl_addr = 0x00000b9c, + .hal_reo1_sw_cookie_cfg0 = 0x0000006c, + .hal_reo1_sw_cookie_cfg1 = 0x00000070, + .hal_reo1_qdesc_lut_base0 = 0x00000074, + .hal_reo1_qdesc_lut_base1 = 0x00000078, .hal_reo1_ring_base_lsb = 0x00000500, .hal_reo1_ring_base_msb = 0x00000504, .hal_reo1_ring_id = 0x00000508, @@ -757,6 +1101,39 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { /* REO status ring address */ .hal_reo_status_ring_base = 0x00000aa0, + + /* WBM idle link ring address */ + .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 = 0x0000027c, + + /* SW2WBM release ring address */ + .hal_wbm_sw_release_ring_base_lsb = 0x0000037c, + + /* WBM2SW release ring address */ + .hal_wbm0_release_ring_base_lsb = 0x00000e08, + .hal_wbm1_release_ring_base_lsb = 0x00000e80, + + /* PPE release ring address */ + .hal_ppe_rel_ring_base = 0x0000046c, + + /* CE address */ + .hal_umac_ce0_src_reg_base = 0x00740000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, + .hal_umac_ce0_dest_reg_base = 0x00741000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, + .hal_umac_ce1_src_reg_base = 0x00742000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, + .hal_umac_ce1_dest_reg_base = 0x00743000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, }; static const struct ath12k_hw_regs wcn7850_regs = { @@ -771,6 +1148,9 @@ static const struct ath12k_hw_regs wcn7850_regs = { .hal_tcl1_ring_msi1_base_msb = 0x0000094c, .hal_tcl1_ring_msi1_data = 0x00000950, .hal_tcl_ring_base_lsb = 0x00000b58, + .hal_tcl1_ring_base_lsb = 0x00000900, + .hal_tcl1_ring_base_msb = 0x00000904, + .hal_tcl2_ring_base_lsb = 0x00000978, /* TCL STATUS ring address */ .hal_tcl_status_ring_base_lsb = 0x00000d38, @@ -833,6 +1213,14 @@ static const struct ath12k_hw_regs wcn7850_regs = { /* REO status ring address */ .hal_reo_status_ring_base = 0x00000a84, + + /* 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 = 0x1e40304, }; static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274 = { @@ -852,6 +1240,26 @@ static const struct ath12k_hw_hal_params ath12k_hw_hal_params_wcn7850 = { HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN, }; +static const struct ath12k_hw_hal_params ath12k_hw_hal_params_ipq5332 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, + .wbm2sw_cc_enable = HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW0_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW1_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW2_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW3_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN, +}; + +static const struct ce_ie_addr ath12k_ce_ie_addr_ipq5332 = { + .ie1_reg_addr = CE_HOST_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie2_reg_addr = CE_HOST_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie3_reg_addr = CE_HOST_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, +}; + +static const struct ce_remap ath12k_ce_remap_ipq5332 = { + .base = HAL_IPQ5332_CE_WFSS_REG_BASE, + .size = HAL_IPQ5332_CE_SIZE, +}; + static const struct ath12k_hw_params ath12k_hw_params[] = { { .name = "qcn9274 hw1.0", @@ -860,6 +1268,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dir = "QCN9274/hw1.0", .board_size = 256 * 1024, .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, }, .max_radios = 1, .single_pdev_only = false, @@ -880,21 +1289,22 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .hal_params = &ath12k_hw_hal_params_qcn9274, .rxdma1_enable = false, - .num_rxmda_per_pdev = 1, + .num_rxdma_per_pdev = 1, .num_rxdma_dst_ring = 0, .rx_mac_buf_ring = false, .vdev_start_delay = false, .interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT), + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_AP_VLAN), .supports_monitor = false, .idle_ps = false, .download_calib = true, .supports_suspend = false, .tcl_ring_retry = true, - .reoq_lut_support = false, + .reoq_lut_support = true, .supports_shadow_regs = false, .num_tcl_banks = 48, @@ -912,7 +1322,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .rfkill_cfg = 0, .rfkill_on_level = 0, - .rddm_size = 0, + .rddm_size = 0x600000, .def_num_link = 0, .max_mlo_peer = 256, @@ -920,6 +1330,21 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .otp_board_id_register = QCN9274_QFPROM_RAW_RFA_PDET_ROW13_LSB, .supports_sta_ps = false, + + .acpi_guid = NULL, + .supports_dynamic_smps_6ghz = true, + + .iova_mask = 0, + + .supports_aspm = false, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = false, + + .dp_primary_link_only = true, }, { .name = "wcn7850 hw2.0", @@ -929,6 +1354,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dir = "WCN7850/hw2.0", .board_size = 256 * 1024, .cal_offset = 256 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, }, .max_radios = 1, @@ -950,7 +1376,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .hal_params = &ath12k_hw_hal_params_wcn7850, .rxdma1_enable = false, - .num_rxmda_per_pdev = 2, + .num_rxdma_per_pdev = 2, .num_rxdma_dst_ring = 1, .rx_mac_buf_ring = true, .vdev_start_delay = true, @@ -960,11 +1386,11 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { BIT(NL80211_IFTYPE_P2P_DEVICE) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), - .supports_monitor = false, + .supports_monitor = true, .idle_ps = true, .download_calib = false, - .supports_suspend = false, + .supports_suspend = true, .tcl_ring_retry = false, .reoq_lut_support = false, .supports_shadow_regs = true, @@ -993,6 +1419,21 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .otp_board_id_register = 0, .supports_sta_ps = true, + + .acpi_guid = &wcn7850_uuid, + .supports_dynamic_smps_6ghz = false, + + .iova_mask = ATH12K_PCIE_MAX_PAYLOAD_SIZE - 1, + + .supports_aspm = true, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = true, + + .dp_primary_link_only = false, }, { .name = "qcn9274 hw2.0", @@ -1001,6 +1442,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dir = "QCN9274/hw2.0", .board_size = 256 * 1024, .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, }, .max_radios = 2, .single_pdev_only = false, @@ -1020,16 +1462,17 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .hal_params = &ath12k_hw_hal_params_qcn9274, - .rxdma1_enable = false, - .num_rxmda_per_pdev = 1, + .rxdma1_enable = true, + .num_rxdma_per_pdev = 1, .num_rxdma_dst_ring = 0, .rx_mac_buf_ring = false, .vdev_start_delay = false, .interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT), - .supports_monitor = false, + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_AP_VLAN), + .supports_monitor = true, .idle_ps = false, .download_calib = true, @@ -1053,7 +1496,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .rfkill_cfg = 0, .rfkill_on_level = 0, - .rddm_size = 0, + .rddm_size = 0x600000, .def_num_link = 0, .max_mlo_peer = 256, @@ -1061,6 +1504,99 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .otp_board_id_register = QCN9274_QFPROM_RAW_RFA_PDET_ROW13_LSB, .supports_sta_ps = false, + + .acpi_guid = NULL, + .supports_dynamic_smps_6ghz = true, + + .iova_mask = 0, + + .supports_aspm = false, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = false, + + .dp_primary_link_only = true, + }, + { + .name = "ipq5332 hw1.0", + .hw_rev = ATH12K_HW_IPQ5332_HW10, + .fw = { + .dir = "IPQ5332/hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_remoteproc, + }, + .max_radios = 1, + .single_pdev_only = false, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332, + .internal_sleep_clock = false, + + .hw_ops = &qcn9274_ops, + .regs = &ipq5332_regs, + .ring_mask = &ath12k_hw_ring_mask_ipq5332, + + .host_ce_config = ath12k_host_ce_config_ipq5332, + .ce_count = 12, + .target_ce_config = ath12k_target_ce_config_wlan_ipq5332, + .target_ce_count = 12, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_ipq5332, + .svc_to_ce_map_len = 18, + + .hal_params = &ath12k_hw_hal_params_ipq5332, + + .rxdma1_enable = false, + .num_rxdma_per_pdev = 1, + .num_rxdma_dst_ring = 0, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = false, + + .idle_ps = false, + .download_calib = true, + .supports_suspend = false, + .tcl_ring_retry = true, + .reoq_lut_support = false, + .supports_shadow_regs = false, + + .num_tcl_banks = 48, + .max_tx_ring = 4, + + .wmi_init = &ath12k_wmi_init_qcn9274, + + .hal_ops = &hal_qcn9274_ops, + + .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01), + + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, + + .rddm_size = 0, + + .def_num_link = 0, + .max_mlo_peer = 256, + + .otp_board_id_register = 0, + + .supports_sta_ps = false, + + .acpi_guid = NULL, + .supports_dynamic_smps_6ghz = false, + .iova_mask = 0, + .supports_aspm = false, + + .ce_ie_addr = &ath12k_ce_ie_addr_ipq5332, + .ce_remap = &ath12k_ce_remap_ipq5332, + .bdf_addr_offset = 0xC00000, + + .dp_primary_link_only = true, }, }; diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 87965980b938..0a75bc5abfa2 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -1,13 +1,14 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HW_H #define ATH12K_HW_H #include <linux/mhi.h> +#include <linux/uuid.h> #include "wmi.h" #include "hal.h" @@ -77,9 +78,9 @@ #define TARGET_NUM_WDS_ENTRIES 32 #define TARGET_DMA_BURST_SIZE 1 #define TARGET_RX_BATCHMODE 1 -#define TARGET_RX_PEER_METADATA_VER_V1A 2 -#define TARGET_RX_PEER_METADATA_VER_V1B 3 +#define TARGET_EMA_MAX_PROFILE_PERIOD 8 +#define ATH12K_HW_DEFAULT_QUEUE 0 #define ATH12K_HW_MAX_QUEUES 4 #define ATH12K_QUEUE_LEN 4096 @@ -95,6 +96,9 @@ #define ATH12K_M3_FILE "m3.bin" #define ATH12K_REGDB_FILE_NAME "regdb.bin" +#define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128 +#define ATH12K_IPQ5332_USERPD_ID 1 + enum ath12k_hw_rate_cck { ATH12K_HW_RATE_CCK_LP_11M = 0, ATH12K_HW_RATE_CCK_LP_5_5M, @@ -118,6 +122,7 @@ enum ath12k_hw_rate_ofdm { enum ath12k_bus { ATH12K_BUS_PCI, + ATH12K_BUS_AHB, }; #define ATH12K_EXT_IRQ_GRP_NUM_MAX 11 @@ -130,6 +135,7 @@ enum hal_encrypt_type; struct ath12k_hw_ring_mask { u8 tx[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx_mon_dest[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx_mon_status[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx_err[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx_wbm_rel[ATH12K_EXT_IRQ_GRP_NUM_MAX]; @@ -143,6 +149,11 @@ struct ath12k_hw_hal_params { u32 wbm2sw_cc_enable; }; +enum ath12k_m3_fw_loaders { + ath12k_m3_fw_loader_driver, + ath12k_m3_fw_loader_remoteproc, +}; + struct ath12k_hw_params { const char *name; u16 hw_rev; @@ -151,6 +162,7 @@ struct ath12k_hw_params { const char *dir; size_t board_size; size_t cal_offset; + enum ath12k_m3_fw_loaders m3_loader; } fw; u8 max_radios; @@ -172,7 +184,7 @@ struct ath12k_hw_params { const struct ath12k_hw_hal_params *hal_params; bool rxdma1_enable:1; - int num_rxmda_per_pdev; + int num_rxdma_per_pdev; int num_rxdma_dst_ring; bool rx_mac_buf_ring:1; bool vdev_start_delay:1; @@ -186,6 +198,8 @@ struct ath12k_hw_params { bool tcl_ring_retry:1; bool reoq_lut_support:1; bool supports_shadow_regs:1; + bool supports_aspm:1; + bool current_cc_support:1; u32 num_tcl_banks; u32 max_tx_ring; @@ -211,6 +225,18 @@ struct ath12k_hw_params { u32 otp_board_id_register; bool supports_sta_ps; + + const guid_t *acpi_guid; + bool supports_dynamic_smps_6ghz; + + u32 iova_mask; + + const struct ce_ie_addr *ce_ie_addr; + const struct ce_remap *ce_remap; + u32 bdf_addr_offset; + + /* setup REO queue, frag etc only for primary link peer */ + bool dp_primary_link_only:1; }; struct ath12k_hw_ops { @@ -284,9 +310,15 @@ struct ath12k_hw_regs { u32 hal_tcl1_ring_msi1_base_msb; u32 hal_tcl1_ring_msi1_data; u32 hal_tcl_ring_base_lsb; + u32 hal_tcl1_ring_base_lsb; + u32 hal_tcl1_ring_base_msb; + u32 hal_tcl2_ring_base_lsb; u32 hal_tcl_status_ring_base_lsb; + u32 hal_reo1_qdesc_addr; + u32 hal_reo1_qdesc_max_peerid; + u32 hal_wbm_idle_ring_base_lsb; u32 hal_wbm_idle_ring_misc_addr; u32 hal_wbm_r0_idle_list_cntl_addr; @@ -307,6 +339,11 @@ struct ath12k_hw_regs { u32 pcie_qserdes_sysclk_en_sel; u32 pcie_pcs_osc_dtct_config_base; + u32 hal_umac_ce0_src_reg_base; + u32 hal_umac_ce0_dest_reg_base; + u32 hal_umac_ce1_src_reg_base; + u32 hal_umac_ce1_dest_reg_base; + u32 hal_ppe_rel_ring_base; u32 hal_reo2_ring_base; @@ -338,6 +375,8 @@ struct ath12k_hw_regs { u32 hal_reo_cmd_ring_base; u32 hal_reo_status_ring_base; + + u32 gcc_gcc_pcie_hot_rst; }; static inline const char *ath12k_bd_ie_type_str(enum ath12k_bd_ie_type type) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 52a5fb8b03e9..59ec422992d3 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <net/mac80211.h> +#include <net/cfg80211.h> #include <linux/etherdevice.h> + #include "mac.h" #include "core.h" #include "debug.h" @@ -13,7 +15,12 @@ #include "hw.h" #include "dp_tx.h" #include "dp_rx.h" +#include "testmode.h" #include "peer.h" +#include "debugfs.h" +#include "hif.h" +#include "wow.h" +#include "debugfs_sta.h" #define CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ @@ -90,6 +97,10 @@ static const struct ieee80211_channel ath12k_5ghz_channels[] = { }; static const struct ieee80211_channel ath12k_6ghz_channels[] = { + /* Operating Class 136 */ + CHAN6G(2, 5935, 0), + + /* Operating Classes 131-135 */ CHAN6G(1, 5955, 0), CHAN6G(5, 5975, 0), CHAN6G(9, 5995, 0), @@ -218,7 +229,8 @@ ath12k_phymodes[NUM_NL80211_BANDS][ATH12K_CHAN_WIDTH_NUM] = { const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default = { .rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START | HTT_RX_FILTER_TLV_FLAGS_PPDU_END | - HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE, + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO, .pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0, .pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1, .pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2, @@ -242,7 +254,10 @@ static const u32 ath12k_smps_map[] = { }; static int ath12k_start_vdev_delay(struct ath12k *ar, - struct ath12k_vif *arvif); + struct ath12k_link_vif *arvif); +static void ath12k_mac_stop(struct ath12k *ar); +static int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif); +static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ath12k_link_vif *arvif); static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode) { @@ -325,6 +340,82 @@ static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode) return "<unknown>"; } +u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones) +{ + switch (tones) { + case 26: + return RU_26; + case 52: + return RU_52; + case 106: + return RU_106; + case 242: + return RU_242; + case 484: + return RU_484; + case 996: + return RU_996; + case (996 * 2): + return RU_2X996; + default: + return RU_26; + } +} + +enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi) +{ + switch (sgi) { + case RX_MSDU_START_SGI_0_8_US: + return NL80211_RATE_INFO_EHT_GI_0_8; + case RX_MSDU_START_SGI_1_6_US: + return NL80211_RATE_INFO_EHT_GI_1_6; + case RX_MSDU_START_SGI_3_2_US: + return NL80211_RATE_INFO_EHT_GI_3_2; + default: + return NL80211_RATE_INFO_EHT_GI_0_8; + } +} + +enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones) +{ + switch (ru_tones) { + case 26: + return NL80211_RATE_INFO_EHT_RU_ALLOC_26; + case 52: + return NL80211_RATE_INFO_EHT_RU_ALLOC_52; + case (52 + 26): + return NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; + case 106: + return NL80211_RATE_INFO_EHT_RU_ALLOC_106; + case (106 + 26): + return NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; + case 242: + return NL80211_RATE_INFO_EHT_RU_ALLOC_242; + case 484: + return NL80211_RATE_INFO_EHT_RU_ALLOC_484; + case (484 + 242): + return NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; + case 996: + return NL80211_RATE_INFO_EHT_RU_ALLOC_996; + case (996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; + case (996 + 484 + 242): + return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; + case (2 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; + case (2 * 996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; + case (3 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; + case (3 * 996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; + case (4 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; + default: + return NL80211_RATE_INFO_EHT_RU_ALLOC_26; + } +} + enum rate_info_bw ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw) { @@ -465,24 +556,87 @@ static u8 ath12k_parse_mpdudensity(u8 mpdudensity) } } -static int ath12k_mac_vif_chan(struct ieee80211_vif *vif, - struct cfg80211_chan_def *def) +static int ath12k_mac_vif_link_chan(struct ieee80211_vif *vif, u8 link_id, + struct cfg80211_chan_def *def) { + struct ieee80211_bss_conf *link_conf; struct ieee80211_chanctx_conf *conf; rcu_read_lock(); - conf = rcu_dereference(vif->bss_conf.chanctx_conf); + link_conf = rcu_dereference(vif->link_conf[link_id]); + + if (!link_conf) { + rcu_read_unlock(); + return -ENOLINK; + } + + conf = rcu_dereference(link_conf->chanctx_conf); if (!conf) { rcu_read_unlock(); return -ENOENT; } - *def = conf->def; rcu_read_unlock(); return 0; } +static struct ath12k_link_vif * +ath12k_mac_get_tx_arvif(struct ath12k_link_vif *arvif, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_bss_conf *tx_bss_conf; + struct ath12k *ar = arvif->ar; + struct ath12k_vif *tx_ahvif; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + tx_bss_conf = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + link_conf->tx_bss_conf); + if (tx_bss_conf) { + tx_ahvif = ath12k_vif_to_ahvif(tx_bss_conf->vif); + return wiphy_dereference(tx_ahvif->ah->hw->wiphy, + tx_ahvif->link[tx_bss_conf->link_id]); + } + + return NULL; +} + +struct ieee80211_bss_conf * +ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif) +{ + struct ieee80211_vif *vif = arvif->ahvif->vif; + struct ieee80211_bss_conf *link_conf; + struct ath12k *ar = arvif->ar; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (arvif->link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + link_conf = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + vif->link_conf[arvif->link_id]); + + return link_conf; +} + +static struct ieee80211_link_sta *ath12k_mac_get_link_sta(struct ath12k_link_sta *arsta) +{ + struct ath12k_sta *ahsta = arsta->ahsta; + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); + struct ieee80211_link_sta *link_sta; + + lockdep_assert_wiphy(ahsta->ahvif->ah->hw->wiphy); + + if (arsta->link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + link_sta = wiphy_dereference(ahsta->ahvif->ah->hw->wiphy, + sta->link[arsta->link_id]); + + return link_sta; +} + static bool ath12k_mac_bitrate_is_cck(int bitrate) { switch (bitrate) { @@ -528,18 +682,35 @@ static void ath12k_get_arvif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath12k_vif_iter *arvif_iter = data; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + unsigned long links_map = ahvif->links_map; + struct ath12k_link_vif *arvif; + u8 link_id; + + for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = rcu_dereference(ahvif->link[link_id]); - if (arvif->vdev_id == arvif_iter->vdev_id) - arvif_iter->arvif = arvif; + if (WARN_ON(!arvif)) + continue; + + if (arvif->vdev_id == arvif_iter->vdev_id && + arvif->ar == arvif_iter->ar) { + arvif_iter->arvif = arvif; + break; + } + } } -struct ath12k_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) +struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) { struct ath12k_vif_iter arvif_iter = {}; u32 flags; + /* To use the arvif returned, caller must have held rcu read lock. + */ + WARN_ON(!rcu_read_lock_any_held()); arvif_iter.vdev_id = vdev_id; + arvif_iter.ar = ar; flags = IEEE80211_IFACE_ITER_RESUME_ALL; ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar), @@ -554,12 +725,12 @@ struct ath12k_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) return arvif_iter.arvif; } -struct ath12k_vif *ath12k_mac_get_arvif_by_vdev_id(struct ath12k_base *ab, - u32 vdev_id) +struct ath12k_link_vif *ath12k_mac_get_arvif_by_vdev_id(struct ath12k_base *ab, + u32 vdev_id) { int i; struct ath12k_pdev *pdev; - struct ath12k_vif *arvif; + struct ath12k_link_vif *arvif; for (i = 0; i < ab->num_radios; i++) { pdev = rcu_dereference(ab->pdevs_active[i]); @@ -604,7 +775,10 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) return NULL; for (i = 0; i < ab->num_radios; i++) { - pdev = rcu_dereference(ab->pdevs_active[i]); + if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM) + pdev = &ab->pdevs[i]; + else + pdev = rcu_dereference(ab->pdevs_active[i]); if (pdev && pdev->pdev_id == pdev_id) return (pdev->ar ? pdev->ar : NULL); @@ -613,6 +787,161 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) return NULL; } +static bool ath12k_mac_is_ml_arvif(struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + + lockdep_assert_wiphy(ahvif->ah->hw->wiphy); + + if (ahvif->vif->valid_links & BIT(arvif->link_id)) + return true; + + return false; +} + +static struct ath12k *ath12k_mac_get_ar_by_chan(struct ieee80211_hw *hw, + struct ieee80211_channel *channel) +{ + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar; + int i; + + ar = ah->radio; + + if (ah->num_radio == 1) + return ar; + + for_each_ar(ah, ar, i) { + if (channel->center_freq >= KHZ_TO_MHZ(ar->freq_range.start_freq) && + channel->center_freq <= KHZ_TO_MHZ(ar->freq_range.end_freq)) + return ar; + } + return NULL; +} + +static struct ath12k *ath12k_get_ar_by_ctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + if (!ctx) + return NULL; + + return ath12k_mac_get_ar_by_chan(hw, ctx->def.chan); +} + +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 link_id) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_link_vif *arvif; + + lockdep_assert_wiphy(hw->wiphy); + + /* If there is one pdev within ah, then we return + * ar directly. + */ + if (ah->num_radio == 1) + return ah->radio; + + if (!(ahvif->links_map & BIT(link_id))) + return NULL; + + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (arvif && arvif->is_created) + return arvif->ar; + + return NULL; +} + +void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + void *data) +{ + struct ath12k_mac_get_any_chanctx_conf_arg *arg = data; + struct ath12k *ctx_ar = ath12k_get_ar_by_ctx(hw, conf); + + if (ctx_ar == arg->ar) + arg->chanctx_conf = conf; +} + +static struct ath12k_link_vif *ath12k_mac_get_vif_up(struct ath12k *ar) +{ + struct ath12k_link_vif *arvif; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up) + return arvif; + } + + return NULL; +} + +static bool ath12k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BAND band2) +{ + switch (band1) { + case NL80211_BAND_2GHZ: + if (band2 & WMI_HOST_WLAN_2GHZ_CAP) + return true; + break; + case NL80211_BAND_5GHZ: + case NL80211_BAND_6GHZ: + if (band2 & WMI_HOST_WLAN_5GHZ_CAP) + return true; + break; + default: + return false; + } + + return false; +} + +static u8 ath12k_mac_get_target_pdev_id_from_vif(struct ath12k_link_vif *arvif) +{ + struct ath12k *ar = arvif->ar; + struct ath12k_base *ab = ar->ab; + struct ieee80211_vif *vif = arvif->ahvif->vif; + struct cfg80211_chan_def def; + enum nl80211_band band; + u8 pdev_id = ab->fw_pdev[0].pdev_id; + int i; + + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) + return pdev_id; + + band = def.chan->band; + + for (i = 0; i < ab->fw_pdev_count; i++) { + if (ath12k_mac_band_match(band, ab->fw_pdev[i].supported_bands)) + return ab->fw_pdev[i].pdev_id; + } + + return pdev_id; +} + +u8 ath12k_mac_get_target_pdev_id(struct ath12k *ar) +{ + struct ath12k_link_vif *arvif; + struct ath12k_base *ab = ar->ab; + + if (!ab->hw_params->single_pdev_only) + return ar->pdev->pdev_id; + + arvif = ath12k_mac_get_vif_up(ar); + + /* fw_pdev array has pdev ids derived from phy capability + * service ready event (pdev_and_hw_link_ids). + * If no vif is active, return default first index. + */ + if (!arvif) + return ar->ab->fw_pdev[0].pdev_id; + + /* If active vif is found, return the pdev id matching chandef band */ + return ath12k_mac_get_target_pdev_id_from_vif(arvif); +} + static void ath12k_pdev_caps_update(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; @@ -634,11 +963,11 @@ static void ath12k_pdev_caps_update(struct ath12k *ar) static int ath12k_mac_txpower_recalc(struct ath12k *ar) { struct ath12k_pdev *pdev = ar->pdev; - struct ath12k_vif *arvif; + struct ath12k_link_vif *arvif; int ret, txpower = -1; u32 param; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); list_for_each_entry(arvif, &ar->arvifs, list) { if (arvif->txpower <= 0) @@ -660,7 +989,7 @@ static int ath12k_mac_txpower_recalc(struct ath12k *ar) ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower to set in hw %d\n", txpower / 2); - if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) && + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) && ar->txpower_limit_2g != txpower) { param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G; ret = ath12k_wmi_pdev_set_param(ar, param, @@ -670,7 +999,7 @@ static int ath12k_mac_txpower_recalc(struct ath12k *ar) ar->txpower_limit_2g = txpower; } - if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) && + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) && ar->txpower_limit_5g != txpower) { param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G; ret = ath12k_wmi_pdev_set_param(ar, param, @@ -688,13 +1017,13 @@ fail: return ret; } -static int ath12k_recalc_rtscts_prot(struct ath12k_vif *arvif) +static int ath12k_recalc_rtscts_prot(struct ath12k_link_vif *arvif) { struct ath12k *ar = arvif->ar; u32 vdev_param, rts_cts; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); vdev_param = WMI_VDEV_PARAM_ENABLE_RTSCTS; @@ -727,7 +1056,7 @@ static int ath12k_recalc_rtscts_prot(struct ath12k_vif *arvif) return ret; } -static int ath12k_mac_set_kickout(struct ath12k_vif *arvif) +static int ath12k_mac_set_kickout(struct ath12k_link_vif *arvif) { struct ath12k *ar = arvif->ar; u32 param; @@ -777,11 +1106,14 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar) struct ath12k_peer *peer, *tmp; struct ath12k_base *ab = ar->ab; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); spin_lock_bh(&ab->base_lock); list_for_each_entry_safe(peer, tmp, &ab->peers, list) { - ath12k_dp_rx_peer_tid_cleanup(ar, peer); + /* Skip Rx TID cleanup for self peer */ + if (peer->sta) + ath12k_dp_rx_peer_tid_cleanup(ar, peer); + list_del(&peer->list); kfree(peer); } @@ -793,7 +1125,7 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar) static int ath12k_mac_vdev_setup_sync(struct ath12k *ar) { - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) return -ESHUTDOWN; @@ -810,9 +1142,12 @@ static int ath12k_mac_vdev_setup_sync(struct ath12k *ar) static int ath12k_monitor_vdev_up(struct ath12k *ar, int vdev_id) { + struct ath12k_wmi_vdev_up_params params = {}; int ret; - ret = ath12k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); + params.vdev_id = vdev_id; + params.bssid = ar->mac_addr; + ret = ath12k_wmi_vdev_up(ar, ¶ms); if (ret) { ath12k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n", vdev_id, ret); @@ -829,9 +1164,10 @@ static int ath12k_mac_monitor_vdev_start(struct ath12k *ar, int vdev_id, { struct ieee80211_channel *channel; struct wmi_vdev_start_req_arg arg = {}; + struct ath12k_wmi_vdev_up_params params = {}; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); channel = chandef->chan; arg.vdev_id = vdev_id; @@ -869,7 +1205,9 @@ static int ath12k_mac_monitor_vdev_start(struct ath12k *ar, int vdev_id, return ret; } - ret = ath12k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); + params.vdev_id = vdev_id; + params.bssid = ar->mac_addr; + ret = ath12k_wmi_vdev_up(ar, ¶ms); if (ret) { ath12k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n", vdev_id, ret); @@ -892,7 +1230,7 @@ static int ath12k_mac_monitor_vdev_stop(struct ath12k *ar) { int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); reinit_completion(&ar->vdev_setup_done); @@ -916,81 +1254,12 @@ static int ath12k_mac_monitor_vdev_stop(struct ath12k *ar) return ret; } -static int ath12k_mac_monitor_vdev_create(struct ath12k *ar) -{ - struct ath12k_pdev *pdev = ar->pdev; - struct ath12k_wmi_vdev_create_arg arg = {}; - int bit, ret; - u8 tmp_addr[6]; - u16 nss; - - lockdep_assert_held(&ar->conf_mutex); - - if (ar->monitor_vdev_created) - return 0; - - if (ar->ab->free_vdev_map == 0) { - ath12k_warn(ar->ab, "failed to find free vdev id for monitor vdev\n"); - return -ENOMEM; - } - - bit = __ffs64(ar->ab->free_vdev_map); - - ar->monitor_vdev_id = bit; - - arg.if_id = ar->monitor_vdev_id; - arg.type = WMI_VDEV_TYPE_MONITOR; - arg.subtype = WMI_VDEV_SUBTYPE_NONE; - arg.pdev_id = pdev->pdev_id; - arg.if_stats_id = ATH12K_INVAL_VDEV_STATS_ID; - - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { - arg.chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; - arg.chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; - } - - if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { - arg.chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; - arg.chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; - } - - ret = ath12k_wmi_vdev_create(ar, tmp_addr, &arg); - if (ret) { - ath12k_warn(ar->ab, "failed to request monitor vdev %i creation: %d\n", - ar->monitor_vdev_id, ret); - ar->monitor_vdev_id = -1; - return ret; - } - - nss = hweight32(ar->cfg_tx_chainmask) ? : 1; - ret = ath12k_wmi_vdev_set_param_cmd(ar, ar->monitor_vdev_id, - WMI_VDEV_PARAM_NSS, nss); - if (ret) { - ath12k_warn(ar->ab, "failed to set vdev %d chainmask 0x%x, nss %d :%d\n", - ar->monitor_vdev_id, ar->cfg_tx_chainmask, nss, ret); - return ret; - } - - ret = ath12k_mac_txpower_recalc(ar); - if (ret) - return ret; - - ar->allocated_vdev_map |= 1LL << ar->monitor_vdev_id; - ar->ab->free_vdev_map &= ~(1LL << ar->monitor_vdev_id); - ar->num_created_vdevs++; - ar->monitor_vdev_created = true; - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %d created\n", - ar->monitor_vdev_id); - - return 0; -} - static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar) { int ret; unsigned long time_left; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (!ar->monitor_vdev_created) return 0; @@ -1021,52 +1290,50 @@ static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar) return ret; } -static void -ath12k_mac_get_any_chandef_iter(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *conf, - void *data) -{ - struct cfg80211_chan_def **def = data; - - *def = &conf->def; -} - static int ath12k_mac_monitor_start(struct ath12k *ar) { - struct cfg80211_chan_def *chandef = NULL; + struct ath12k_mac_get_any_chanctx_conf_arg arg; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (ar->monitor_started) return 0; + arg.ar = ar; + arg.chanctx_conf = NULL; ieee80211_iter_chan_contexts_atomic(ath12k_ar_to_hw(ar), - ath12k_mac_get_any_chandef_iter, - &chandef); - if (!chandef) + ath12k_mac_get_any_chanctx_conf_iter, + &arg); + if (!arg.chanctx_conf) return 0; - ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, chandef); + ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, + &arg.chanctx_conf->def); if (ret) { ath12k_warn(ar->ab, "failed to start monitor vdev: %d\n", ret); - ath12k_mac_monitor_vdev_delete(ar); + return ret; + } + + ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false); + if (ret) { + ath12k_warn(ar->ab, "fail to set monitor filter: %d\n", ret); return ret; } ar->monitor_started = true; ar->num_started_vdevs++; - ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started ret %d\n", ret); - return ret; + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started\n"); + + return 0; } static int ath12k_mac_monitor_stop(struct ath12k *ar) { int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (!ar->monitor_started) return 0; @@ -1084,12 +1351,13 @@ static int ath12k_mac_monitor_stop(struct ath12k *ar) return ret; } -static int ath12k_mac_vdev_stop(struct ath12k_vif *arvif) +int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif) { + struct ath12k_vif *ahvif = arvif->ahvif; struct ath12k *ar = arvif->ar; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); reinit_completion(&ar->vdev_setup_done); @@ -1111,10 +1379,10 @@ static int ath12k_mac_vdev_stop(struct ath12k_vif *arvif) ar->num_started_vdevs--; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "vdev %pM stopped, vdev_id %d\n", - arvif->vif->addr, arvif->vdev_id); + ahvif->vif->addr, arvif->vdev_id); - if (test_bit(ATH12K_CAC_RUNNING, &ar->dev_flags)) { - clear_bit(ATH12K_CAC_RUNNING, &ar->dev_flags); + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) { + clear_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags); ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "CAC Stopped for vdev %d\n", arvif->vdev_id); } @@ -1124,62 +1392,12 @@ err: return ret; } -static int ath12k_mac_config(struct ath12k *ar, u32 changed) -{ - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); - struct ieee80211_conf *conf = &hw->conf; - int ret = 0; - - mutex_lock(&ar->conf_mutex); - - if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - ar->monitor_conf_enabled = conf->flags & IEEE80211_CONF_MONITOR; - if (ar->monitor_conf_enabled) { - if (ar->monitor_vdev_created) - goto exit; - ret = ath12k_mac_monitor_vdev_create(ar); - if (ret) - goto exit; - ret = ath12k_mac_monitor_start(ar); - if (ret) - goto err_mon_del; - } else { - if (!ar->monitor_vdev_created) - goto exit; - ret = ath12k_mac_monitor_stop(ar); - if (ret) - goto exit; - ath12k_mac_monitor_vdev_delete(ar); - } - } - -exit: - mutex_unlock(&ar->conf_mutex); - return ret; - -err_mon_del: - ath12k_mac_monitor_vdev_delete(ar); - mutex_unlock(&ar->conf_mutex); - return ret; -} - static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar; - int ret; - - ar = ath12k_ah_to_ar(ah); - - ret = ath12k_mac_config(ar, changed); - if (ret) - ath12k_warn(ar->ab, "failed to update config pdev idx %d: %d\n", - ar->pdev_idx, ret); - - return ret; + return 0; } -static int ath12k_mac_setup_bcn_p2p_ie(struct ath12k_vif *arvif, +static int ath12k_mac_setup_bcn_p2p_ie(struct ath12k_link_vif *arvif, struct sk_buff *bcn) { struct ath12k *ar = arvif->ar; @@ -1236,39 +1454,202 @@ static int ath12k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui, return 0; } -static int ath12k_mac_setup_bcn_tmpl(struct ath12k_vif *arvif) +static void ath12k_mac_set_arvif_ies(struct ath12k_link_vif *arvif, struct sk_buff *bcn, + u8 bssid_index, bool *nontx_profile_found) { + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)bcn->data; + const struct element *elem, *nontx, *index, *nie; + const u8 *start, *tail; + u16 rem_len; + u8 i; + + start = bcn->data + ieee80211_get_hdrlen_from_skb(bcn) + sizeof(mgmt->u.beacon); + tail = skb_tail_pointer(bcn); + rem_len = tail - start; + + arvif->rsnie_present = false; + arvif->wpaie_present = false; + + if (cfg80211_find_ie(WLAN_EID_RSN, start, rem_len)) + arvif->rsnie_present = true; + if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, + start, rem_len)) + arvif->wpaie_present = true; + + /* Return from here for the transmitted profile */ + if (!bssid_index) + return; + + /* Initial rsnie_present for the nontransmitted profile is set to be same as that + * of the transmitted profile. It will be changed if security configurations are + * different. + */ + *nontx_profile_found = false; + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, rem_len) { + /* Fixed minimum MBSSID element length with at least one + * nontransmitted BSSID profile is 12 bytes as given below; + * 1 (max BSSID indicator) + + * 2 (Nontransmitted BSSID profile: Subelement ID + length) + + * 4 (Nontransmitted BSSID Capabilities: tag + length + info) + * 2 (Nontransmitted BSSID SSID: tag + length) + * 3 (Nontransmitted BSSID Index: tag + length + BSSID index + */ + if (elem->datalen < 12 || elem->data[0] < 1) + continue; /* Max BSSID indicator must be >=1 */ + + for_each_element(nontx, elem->data + 1, elem->datalen - 1) { + start = nontx->data; + + if (nontx->id != 0 || nontx->datalen < 4) + continue; /* Invalid nontransmitted profile */ + + if (nontx->data[0] != WLAN_EID_NON_TX_BSSID_CAP || + nontx->data[1] != 2) { + continue; /* Missing nontransmitted BSS capabilities */ + } + + if (nontx->data[4] != WLAN_EID_SSID) + continue; /* Missing SSID for nontransmitted BSS */ + + index = cfg80211_find_elem(WLAN_EID_MULTI_BSSID_IDX, + start, nontx->datalen); + if (!index || index->datalen < 1 || index->data[0] == 0) + continue; /* Invalid MBSSID Index element */ + + if (index->data[0] == bssid_index) { + *nontx_profile_found = true; + if (cfg80211_find_ie(WLAN_EID_RSN, + nontx->data, + nontx->datalen)) { + arvif->rsnie_present = true; + return; + } else if (!arvif->rsnie_present) { + return; /* Both tx and nontx BSS are open */ + } + + nie = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, + nontx->data, + nontx->datalen); + if (!nie || nie->datalen < 2) + return; /* Invalid non-inheritance element */ + + for (i = 1; i < nie->datalen - 1; i++) { + if (nie->data[i] == WLAN_EID_RSN) { + arvif->rsnie_present = false; + break; + } + } + + return; + } + } + } +} + +static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif, + struct ath12k_link_vif *tx_arvif, + u8 bssid_index) +{ + struct ath12k_wmi_bcn_tmpl_ema_arg ema_args; + struct ieee80211_ema_beacons *beacons; + bool nontx_profile_found = false; + int ret = 0; + u8 i; + + beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar), + tx_arvif->ahvif->vif, + tx_arvif->link_id); + if (!beacons || !beacons->cnt) { + ath12k_warn(arvif->ar->ab, + "failed to get ema beacon templates from mac80211\n"); + return -EPERM; + } + + if (tx_arvif == arvif) + ath12k_mac_set_arvif_ies(arvif, beacons->bcn[0].skb, 0, NULL); + + for (i = 0; i < beacons->cnt; i++) { + if (tx_arvif != arvif && !nontx_profile_found) + ath12k_mac_set_arvif_ies(arvif, beacons->bcn[i].skb, + bssid_index, + &nontx_profile_found); + + ema_args.bcn_cnt = beacons->cnt; + ema_args.bcn_index = i; + ret = ath12k_wmi_bcn_tmpl(tx_arvif, &beacons->bcn[i].offs, + beacons->bcn[i].skb, &ema_args); + if (ret) { + ath12k_warn(tx_arvif->ar->ab, + "failed to set ema beacon template id %i error %d\n", + i, ret); + break; + } + } + + if (tx_arvif != arvif && !nontx_profile_found) + ath12k_warn(arvif->ar->ab, + "nontransmitted bssid index %u not found in beacon template\n", + bssid_index); + + ieee80211_beacon_free_ema_list(beacons); + return ret; +} + +static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ieee80211_bss_conf *link_conf; + struct ath12k_link_vif *tx_arvif; struct ath12k *ar = arvif->ar; struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); - struct ieee80211_vif *vif = arvif->vif; struct ieee80211_mutable_offsets offs = {}; + bool nontx_profile_found = false; struct sk_buff *bcn; - struct ieee80211_mgmt *mgmt; - u8 *ies; int ret; - if (arvif->vdev_type != WMI_VDEV_TYPE_AP) + if (ahvif->vdev_type != WMI_VDEV_TYPE_AP) return 0; - bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf to set bcn tmpl for vif %pM link %u\n", + vif->addr, arvif->link_id); + return -ENOLINK; + } + + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (tx_arvif) { + if (tx_arvif != arvif && arvif->is_up) + return 0; + + if (link_conf->ema_ap) + return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif, + link_conf->bssid_index); + } else { + tx_arvif = arvif; + } + + bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), + tx_arvif->ahvif->vif, + &offs, tx_arvif->link_id); if (!bcn) { ath12k_warn(ab, "failed to get beacon template from mac80211\n"); return -EPERM; } - ies = bcn->data + ieee80211_get_hdrlen_from_skb(bcn); - ies += sizeof(mgmt->u.beacon); - - if (cfg80211_find_ie(WLAN_EID_RSN, ies, (skb_tail_pointer(bcn) - ies))) - arvif->rsnie_present = true; - - if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPA, - ies, (skb_tail_pointer(bcn) - ies))) - arvif->wpaie_present = true; + if (tx_arvif == arvif) { + ath12k_mac_set_arvif_ies(arvif, bcn, 0, NULL); + } else { + ath12k_mac_set_arvif_ies(arvif, bcn, + link_conf->bssid_index, + &nontx_profile_found); + if (!nontx_profile_found) + ath12k_warn(ab, + "nontransmitted profile not found in beacon template\n"); + } - if (arvif->vif->type == NL80211_IFTYPE_AP && arvif->vif->p2p) { + if (ahvif->vif->type == NL80211_IFTYPE_AP && ahvif->vif->p2p) { ret = ath12k_mac_setup_bcn_p2p_ie(arvif, bcn); if (ret) { ath12k_warn(ab, "failed to setup P2P GO bcn ie: %d\n", @@ -1291,7 +1672,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_vif *arvif) } } - ret = ath12k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn); + ret = ath12k_wmi_bcn_tmpl(arvif, &offs, bcn, NULL); if (ret) ath12k_warn(ab, "failed to submit beacon template command: %d\n", @@ -1302,13 +1683,17 @@ free_bcn_skb: return ret; } -static void ath12k_control_beaconing(struct ath12k_vif *arvif, +static void ath12k_control_beaconing(struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *info) { + struct ath12k_wmi_vdev_up_params params = {}; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *link_conf; + struct ath12k_link_vif *tx_arvif; struct ath12k *ar = arvif->ar; int ret; - lockdep_assert_held(&arvif->ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(arvif->ar)->wiphy); if (!info->enable_beacon) { ret = ath12k_wmi_vdev_down(ar, arvif->vdev_id); @@ -1328,12 +1713,29 @@ static void ath12k_control_beaconing(struct ath12k_vif *arvif, return; } - arvif->aid = 0; + ahvif->aid = 0; - ether_addr_copy(arvif->bssid, info->bssid); + ether_addr_copy(arvif->bssid, info->addr); + + params.vdev_id = arvif->vdev_id; + params.aid = ahvif->aid; + params.bssid = arvif->bssid; + + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, + "unable to access bss link conf for link %u required to retrieve transmitting link conf\n", + arvif->link_id); + return; + } - ret = ath12k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, - arvif->bssid); + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (tx_arvif) { + params.tx_bssid = tx_arvif->bssid; + params.nontx_profile_idx = info->bssid_index; + params.nontx_profile_cnt = 1 << info->bssid_indicator; + } + ret = ath12k_wmi_vdev_up(arvif->ar, ¶ms); if (ret) { ath12k_warn(ar->ab, "failed to bring up vdev %d: %i\n", arvif->vdev_id, ret); @@ -1345,48 +1747,137 @@ static void ath12k_control_beaconing(struct ath12k_vif *arvif, ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); } +static void ath12k_mac_handle_beacon_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct sk_buff *skb = data; + struct ieee80211_mgmt *mgmt = (void *)skb->data; + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif = &ahvif->deflink; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (!ether_addr_equal(mgmt->bssid, vif->bss_conf.bssid)) + return; + + cancel_delayed_work(&arvif->connection_loss_work); +} + +void ath12k_mac_handle_beacon(struct ath12k *ar, struct sk_buff *skb) +{ + ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar), + IEEE80211_IFACE_ITER_NORMAL, + ath12k_mac_handle_beacon_iter, + skb); +} + +static void ath12k_mac_handle_beacon_miss_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + u32 *vdev_id = data; + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif = &ahvif->deflink; + struct ath12k *ar = arvif->ar; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + + if (arvif->vdev_id != *vdev_id) + return; + + if (!arvif->is_up) + return; + + ieee80211_beacon_loss(vif); + + /* Firmware doesn't report beacon loss events repeatedly. If AP probe + * (done by mac80211) succeeds but beacons do not resume then it + * doesn't make sense to continue operation. Queue connection loss work + * which can be cancelled when beacon is received. + */ + ieee80211_queue_delayed_work(hw, &arvif->connection_loss_work, + ATH12K_CONNECTION_LOSS_HZ); +} + +void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id) +{ + ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar), + IEEE80211_IFACE_ITER_NORMAL, + ath12k_mac_handle_beacon_miss_iter, + &vdev_id); +} + +static void ath12k_mac_vif_sta_connection_loss_work(struct work_struct *work) +{ + struct ath12k_link_vif *arvif = container_of(work, struct ath12k_link_vif, + connection_loss_work.work); + struct ieee80211_vif *vif = arvif->ahvif->vif; + + if (!arvif->is_up) + return; + + ieee80211_connection_loss(vif); +} + static void ath12k_peer_assoc_h_basic(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + struct ieee80211_bss_conf *bss_conf; u32 aid; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); if (vif->type == NL80211_IFTYPE_STATION) aid = vif->cfg.aid; else aid = sta->aid; - ether_addr_copy(arg->peer_mac, sta->addr); + ether_addr_copy(arg->peer_mac, arsta->addr); arg->vdev_id = arvif->vdev_id; arg->peer_associd = aid; arg->auth_flag = true; /* TODO: STA WAR in ath10k for listen interval required? */ arg->peer_listen_intval = hw->conf.listen_interval; arg->peer_nss = 1; - arg->peer_caps = vif->bss_conf.assoc_capability; + + bss_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!bss_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in peer assoc for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } + + arg->peer_caps = bss_conf->assoc_capability; } static void ath12k_peer_assoc_h_crypto(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - struct ieee80211_bss_conf *info = &vif->bss_conf; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct ieee80211_bss_conf *info; struct cfg80211_chan_def def; struct cfg80211_bss *bss; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); const u8 *rsnie = NULL; const u8 *wpaie = NULL; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); - if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + info = ath12k_mac_get_link_bss_conf(arvif); + if (!info) { + ath12k_warn(ar->ab, "unable to access bss link conf for peer assoc crypto for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } + + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return; bss = cfg80211_get_bss(hw->wiphy, def.chan, info->bssid, NULL, 0, @@ -1434,12 +1925,14 @@ static void ath12k_peer_assoc_h_crypto(struct ath12k *ar, } static void ath12k_peer_assoc_h_rates(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates; + struct ieee80211_link_sta *link_sta; struct cfg80211_chan_def def; const struct ieee80211_supported_band *sband; const struct ieee80211_rate *rates; @@ -1449,14 +1942,21 @@ static void ath12k_peer_assoc_h_rates(struct ath12k *ar, u8 rate; int i; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); - if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return; + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc rates for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + band = def.chan->band; sband = hw->wiphy->bands[band]; - ratemask = sta->deflink.supp_rates[band]; + ratemask = link_sta->supp_rates[band]; ratemask &= arvif->bitrate_mask.control[band].legacy; rates = sband->bitrates; @@ -1497,12 +1997,14 @@ ath12k_peer_assoc_h_vht_masked(const u16 *vht_mcs_mask) } static void ath12k_peer_assoc_h_ht(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + const struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_link_sta *link_sta; struct cfg80211_chan_def def; enum nl80211_band band; const u8 *ht_mcs_mask; @@ -1510,11 +2012,19 @@ static void ath12k_peer_assoc_h_ht(struct ath12k *ar, u8 max_nss; u32 stbc; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) + return; - if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc ht for sta %pM link %u\n", + sta->addr, arsta->link_id); return; + } + ht_cap = &link_sta->ht_cap; if (!ht_cap->ht_supported) return; @@ -1538,7 +2048,7 @@ static void ath12k_peer_assoc_h_ht(struct ath12k *ar, if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) arg->ldpc_flag = true; - if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) { + if (link_sta->bandwidth >= IEEE80211_STA_RX_BW_40) { arg->bw_40 = true; arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG; } @@ -1588,7 +2098,7 @@ static void ath12k_peer_assoc_h_ht(struct ath12k *ar, arg->peer_ht_rates.rates[i] = i; } else { arg->peer_ht_rates.num_rates = n; - arg->peer_nss = min(sta->deflink.rx_nss, max_nss); + arg->peer_nss = min(link_sta->rx_nss, max_nss); } ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", @@ -1658,12 +2168,14 @@ ath12k_peer_assoc_h_vht_limit(u16 tx_mcs_set, } static void ath12k_peer_assoc_h_vht(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + const struct ieee80211_sta_vht_cap *vht_cap; + struct ieee80211_link_sta *link_sta; struct cfg80211_chan_def def; enum nl80211_band band; const u16 *vht_mcs_mask; @@ -1672,9 +2184,19 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar, u8 max_nss, vht_mcs; int i; - if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return; + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc vht for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + + vht_cap = &link_sta->vht_cap; if (!vht_cap->vht_supported) return; @@ -1707,10 +2229,10 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar, (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + ampdu_factor)) - 1); - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_80) arg->bw_80 = true; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) arg->bw_160 = true; /* Calculate peer NSS capability from VHT capabilities if STA @@ -1724,7 +2246,7 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar, vht_mcs_mask[i]) max_nss = i + 1; } - arg->peer_nss = min(sta->deflink.rx_nss, max_nss); + arg->peer_nss = min(link_sta->rx_nss, max_nss); arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest); arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest); @@ -1747,23 +2269,44 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar, arg->tx_max_mcs_nss = 0xFF; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", - sta->addr, arg->peer_max_mpdu, arg->peer_flags); + arsta->addr, arg->peer_max_mpdu, arg->peer_flags); /* TODO: rxnss_override */ } static void ath12k_peer_assoc_h_he(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; int i; - u8 ampdu_factor, rx_mcs_80, rx_mcs_160, max_nss; + u8 ampdu_factor, max_nss; + u8 rx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; + u8 rx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; u16 mcs_160_map, mcs_80_map; bool support_160; u16 v; + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in peer assoc he for vif %pM link %u", + vif->addr, arvif->link_id); + return; + } + + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc he for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + + he_cap = &link_sta->he_cap; if (!he_cap->has_he) return; @@ -1801,13 +2344,13 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar, else max_nss = rx_mcs_80; - arg->peer_nss = min(sta->deflink.rx_nss, max_nss); + arg->peer_nss = min(link_sta->rx_nss, max_nss); memcpy(&arg->peer_he_cap_macinfo, he_cap->he_cap_elem.mac_cap_info, sizeof(he_cap->he_cap_elem.mac_cap_info)); memcpy(&arg->peer_he_cap_phyinfo, he_cap->he_cap_elem.phy_cap_info, sizeof(he_cap->he_cap_elem.phy_cap_info)); - arg->peer_he_ops = vif->bss_conf.he_oper.params; + arg->peer_he_ops = link_conf->he_oper.params; /* the top most byte is used to indicate BSS color info */ arg->peer_he_ops &= 0xffffff; @@ -1824,15 +2367,14 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar, * request, then use MAX_AMPDU_LEN_FACTOR as 16 to calculate max_ampdu * length. */ - ampdu_factor = (he_cap->he_cap_elem.mac_cap_info[3] & - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK) >> - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK; + ampdu_factor = u8_get_bits(he_cap->he_cap_elem.mac_cap_info[3], + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); if (ampdu_factor) { - if (sta->deflink.vht_cap.vht_supported) + if (link_sta->vht_cap.vht_supported) arg->peer_max_mpdu = (1 << (IEEE80211_HE_VHT_MAX_AMPDU_FACTOR + ampdu_factor)) - 1; - else if (sta->deflink.ht_cap.ht_supported) + else if (link_sta->ht_cap.ht_supported) arg->peer_max_mpdu = (1 << (IEEE80211_HE_HT_MAX_AMPDU_FACTOR + ampdu_factor)) - 1; } @@ -1873,7 +2415,7 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar, if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_REQ) arg->twt_requester = true; - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: if (he_cap->he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { @@ -1906,17 +2448,114 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar, } } -static void ath12k_peer_assoc_h_smps(struct ieee80211_sta *sta, +static void ath12k_peer_assoc_h_he_6ghz(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, + struct ath12k_wmi_peer_assoc_arg *arg) +{ + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_link_sta *link_sta; + struct cfg80211_chan_def def; + enum nl80211_band band; + u8 ampdu_factor, mpdu_density; + + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) + return; + + band = def.chan->band; + + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc he 6ghz for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + + he_cap = &link_sta->he_cap; + + if (!arg->he_flag || band != NL80211_BAND_6GHZ || !link_sta->he_6ghz_capa.capa) + return; + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) + arg->bw_40 = true; + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_80) + arg->bw_80 = true; + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) + arg->bw_160 = true; + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) + arg->bw_320 = true; + + arg->peer_he_caps_6ghz = le16_to_cpu(link_sta->he_6ghz_capa.capa); + + mpdu_density = u32_get_bits(arg->peer_he_caps_6ghz, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + arg->peer_mpdu_density = ath12k_parse_mpdudensity(mpdu_density); + + /* From IEEE Std 802.11ax-2021 - Section 10.12.2: An HE STA shall be capable of + * receiving A-MPDU where the A-MPDU pre-EOF padding length is up to the value + * indicated by the Maximum A-MPDU Length Exponent Extension field in the HE + * Capabilities element and the Maximum A-MPDU Length Exponent field in HE 6 GHz + * Band Capabilities element in the 6 GHz band. + * + * Here, we are extracting the Max A-MPDU Exponent Extension from HE caps and + * factor is the Maximum A-MPDU Length Exponent from HE 6 GHZ Band capability. + */ + ampdu_factor = u8_get_bits(he_cap->he_cap_elem.mac_cap_info[3], + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK) + + u32_get_bits(arg->peer_he_caps_6ghz, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + + arg->peer_max_mpdu = (1u << (IEEE80211_HE_6GHZ_MAX_AMPDU_FACTOR + + ampdu_factor)) - 1; +} + +static int ath12k_get_smps_from_capa(const struct ieee80211_sta_ht_cap *ht_cap, + const struct ieee80211_he_6ghz_capa *he_6ghz_capa, + int *smps) +{ + if (ht_cap->ht_supported) + *smps = u16_get_bits(ht_cap->cap, IEEE80211_HT_CAP_SM_PS); + else + *smps = le16_get_bits(he_6ghz_capa->capa, + IEEE80211_HE_6GHZ_CAP_SM_PS); + + if (*smps >= ARRAY_SIZE(ath12k_smps_map)) + return -EINVAL; + + return 0; +} + +static void ath12k_peer_assoc_h_smps(struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + const struct ieee80211_he_6ghz_capa *he_6ghz_capa; + struct ath12k_link_vif *arvif = arsta->arvif; + const struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_link_sta *link_sta; + struct ath12k *ar = arvif->ar; int smps; - if (!ht_cap->ht_supported) + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc he for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + + he_6ghz_capa = &link_sta->he_6ghz_capa; + ht_cap = &link_sta->ht_cap; + + if (!ht_cap->ht_supported && !he_6ghz_capa->capa) return; - smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; - smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + if (ath12k_get_smps_from_capa(ht_cap, he_6ghz_capa, &smps)) + return; switch (smps) { case WLAN_HT_CAP_SM_PS_STATIC: @@ -1934,13 +2573,13 @@ static void ath12k_peer_assoc_h_smps(struct ieee80211_sta *sta, } static void ath12k_peer_assoc_h_qos(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); - switch (arvif->vdev_type) { + switch (arvif->ahvif->vdev_type) { case WMI_VDEV_TYPE_AP: if (sta->wme) { /* TODO: Check WME vs QoS */ @@ -1966,19 +2605,20 @@ static void ath12k_peer_assoc_h_qos(struct ath12k *ar, } ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac peer %pM qos %d\n", - sta->addr, arg->qos_flag); + arsta->addr, arg->qos_flag); } static int ath12k_peer_assoc_qos_ap(struct ath12k *ar, - struct ath12k_vif *arvif, - struct ieee80211_sta *sta) + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) { + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct ath12k_wmi_ap_ps_arg arg; u32 max_sp; u32 uapsd; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); arg.vdev_id = arvif->vdev_id; @@ -2005,26 +2645,26 @@ static int ath12k_peer_assoc_qos_ap(struct ath12k *ar, arg.param = WMI_AP_PS_PEER_PARAM_UAPSD; arg.value = uapsd; - ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, &arg); + ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, arsta->addr, &arg); if (ret) goto err; arg.param = WMI_AP_PS_PEER_PARAM_MAX_SP; arg.value = max_sp; - ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, &arg); + ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, arsta->addr, &arg); if (ret) goto err; /* TODO: revisit during testing */ arg.param = WMI_AP_PS_PEER_PARAM_SIFS_RESP_FRMTYPE; arg.value = DISABLE_SIFS_RESPONSE_TRIGGER; - ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, &arg); + ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, arsta->addr, &arg); if (ret) goto err; arg.param = WMI_AP_PS_PEER_PARAM_SIFS_RESP_UAPSD; arg.value = DISABLE_SIFS_RESPONSE_TRIGGER; - ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, &arg); + ret = ath12k_wmi_send_set_ap_ps_param_cmd(ar, arsta->addr, &arg); if (ret) goto err; @@ -2036,17 +2676,17 @@ err: return ret; } -static bool ath12k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta) +static bool ath12k_mac_sta_has_ofdm_only(struct ieee80211_link_sta *sta) { - return sta->deflink.supp_rates[NL80211_BAND_2GHZ] >> + return sta->supp_rates[NL80211_BAND_2GHZ] >> ATH12K_MAC_FIRST_OFDM_RATE_IDX; } static enum wmi_phy_mode ath12k_mac_get_phymode_vht(struct ath12k *ar, - struct ieee80211_sta *sta) + struct ieee80211_link_sta *link_sta) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) { - switch (sta->deflink.vht_cap.cap & + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) { + switch (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: return MODE_11AC_VHT160; @@ -2058,125 +2698,137 @@ static enum wmi_phy_mode ath12k_mac_get_phymode_vht(struct ath12k *ar, } } - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_80) return MODE_11AC_VHT80; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) return MODE_11AC_VHT40; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) return MODE_11AC_VHT20; return MODE_UNKNOWN; } static enum wmi_phy_mode ath12k_mac_get_phymode_he(struct ath12k *ar, - struct ieee80211_sta *sta) + struct ieee80211_link_sta *link_sta) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) { - if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) { + if (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) return MODE_11AX_HE160; - else if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + else if (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) return MODE_11AX_HE80_80; /* not sure if this is a valid case? */ return MODE_11AX_HE160; } - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_80) return MODE_11AX_HE80; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) return MODE_11AX_HE40; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) return MODE_11AX_HE20; return MODE_UNKNOWN; } static enum wmi_phy_mode ath12k_mac_get_phymode_eht(struct ath12k *ar, - struct ieee80211_sta *sta) + struct ieee80211_link_sta *link_sta) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) - if (sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[0] & + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) + if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) return MODE_11BE_EHT320; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) { - if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) { + if (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) return MODE_11BE_EHT160; - if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + if (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) return MODE_11BE_EHT80_80; ath12k_warn(ar->ab, "invalid EHT PHY capability info for 160 Mhz: %d\n", - sta->deflink.he_cap.he_cap_elem.phy_cap_info[0]); + link_sta->he_cap.he_cap_elem.phy_cap_info[0]); return MODE_11BE_EHT160; } - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_80) return MODE_11BE_EHT80; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) return MODE_11BE_EHT40; - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) return MODE_11BE_EHT20; return MODE_UNKNOWN; } static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_link_sta *link_sta; struct cfg80211_chan_def def; enum nl80211_band band; const u8 *ht_mcs_mask; const u16 *vht_mcs_mask; enum wmi_phy_mode phymode = MODE_UNKNOWN; - if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return; band = def.chan->band; ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc he for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + switch (band) { case NL80211_BAND_2GHZ: - if (sta->deflink.eht_cap.has_eht) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + if (link_sta->eht_cap.has_eht) { + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11BE_EHT40_2G; else phymode = MODE_11BE_EHT20_2G; - } else if (sta->deflink.he_cap.has_he) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) + } else if (link_sta->he_cap.has_he) { + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_80) phymode = MODE_11AX_HE80_2G; - else if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + else if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11AX_HE40_2G; else phymode = MODE_11AX_HE20_2G; - } else if (sta->deflink.vht_cap.vht_supported && + } else if (link_sta->vht_cap.vht_supported && !ath12k_peer_assoc_h_vht_masked(vht_mcs_mask)) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11AC_VHT40; else phymode = MODE_11AC_VHT20; - } else if (sta->deflink.ht_cap.ht_supported && + } else if (link_sta->ht_cap.ht_supported && !ath12k_peer_assoc_h_ht_masked(ht_mcs_mask)) { - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11NG_HT40; else phymode = MODE_11NG_HT20; - } else if (ath12k_mac_sta_has_ofdm_only(sta)) { + } else if (ath12k_mac_sta_has_ofdm_only(link_sta)) { phymode = MODE_11G; } else { phymode = MODE_11B; @@ -2185,16 +2837,16 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, case NL80211_BAND_5GHZ: case NL80211_BAND_6GHZ: /* Check EHT first */ - if (sta->deflink.eht_cap.has_eht) { - phymode = ath12k_mac_get_phymode_eht(ar, sta); - } else if (sta->deflink.he_cap.has_he) { - phymode = ath12k_mac_get_phymode_he(ar, sta); - } else if (sta->deflink.vht_cap.vht_supported && + if (link_sta->eht_cap.has_eht) { + phymode = ath12k_mac_get_phymode_eht(ar, link_sta); + } else if (link_sta->he_cap.has_he) { + phymode = ath12k_mac_get_phymode_he(ar, link_sta); + } else if (link_sta->vht_cap.vht_supported && !ath12k_peer_assoc_h_vht_masked(vht_mcs_mask)) { - phymode = ath12k_mac_get_phymode_vht(ar, sta); - } else if (sta->deflink.ht_cap.ht_supported && + phymode = ath12k_mac_get_phymode_vht(ar, link_sta); + } else if (link_sta->ht_cap.ht_supported && !ath12k_peer_assoc_h_ht_masked(ht_mcs_mask)) { - if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) + if (link_sta->bandwidth >= IEEE80211_STA_RX_BW_40) phymode = MODE_11NA_HT40; else phymode = MODE_11NA_HT20; @@ -2207,7 +2859,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, } ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac peer %pM phymode %s\n", - sta->addr, ath12k_mac_phymode_str(phymode)); + arsta->addr, ath12k_mac_phymode_str(phymode)); arg->peer_phymode = phymode; WARN_ON(phymode == MODE_UNKNOWN); @@ -2277,18 +2929,37 @@ static void ath12k_mac_set_eht_ppe_threshold(const u8 *ppe_thres, } static void ath12k_peer_assoc_h_eht(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg) { - const struct ieee80211_sta_eht_cap *eht_cap = &sta->deflink.eht_cap; - const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); const struct ieee80211_eht_mcs_nss_supp_20mhz_only *bw_20; const struct ieee80211_eht_mcs_nss_supp_bw *bw; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + const struct ieee80211_sta_eht_cap *eht_cap; + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *link_conf; u32 *rx_mcs, *tx_mcs; - if (!sta->deflink.he_cap.has_he || !eht_cap->has_eht) + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc eht for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access link_conf in peer assoc eht set\n"); + return; + } + + eht_cap = &link_sta->eht_cap; + he_cap = &link_sta->he_cap; + if (!he_cap->has_he || !eht_cap->has_eht) return; arg->eht_flag = true; @@ -2307,7 +2978,7 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, rx_mcs = arg->peer_eht_rx_mcs_set; tx_mcs = arg->peer_eht_tx_mcs_set; - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_320: bw = &eht_cap->eht_mcs_nss_supp.bw._320; ath12k_mac_set_eht_mcs(bw->rx_tx_mcs9_max_nss, @@ -2357,86 +3028,211 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, } arg->punct_bitmap = ~arvif->punct_bitmap; + arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15; +} + +static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, + struct ath12k_wmi_peer_assoc_arg *arg) +{ + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct peer_assoc_mlo_params *ml = &arg->ml; + struct ath12k_sta *ahsta = arsta->ahsta; + struct ath12k_link_sta *arsta_p; + struct ath12k_link_vif *arvif; + unsigned long links; + u8 link_id; + int i; + + if (!sta->mlo || ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) + return; + + ml->enabled = true; + ml->assoc_link = arsta->is_assoc_link; + + /* For now considering the primary umac based on assoc link */ + ml->primary_umac = arsta->is_assoc_link; + ml->peer_id_valid = true; + ml->logical_link_idx_valid = true; + + ether_addr_copy(ml->mld_addr, sta->addr); + ml->logical_link_idx = arsta->link_idx; + ml->ml_peer_id = ahsta->ml_peer_id; + ml->ieee_link_id = arsta->link_id; + ml->num_partner_links = 0; + ml->eml_cap = sta->eml_cap; + links = ahsta->links_map; + + rcu_read_lock(); + + i = 0; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + if (i >= ATH12K_WMI_MLO_MAX_LINKS) + break; + + arsta_p = rcu_dereference(ahsta->link[link_id]); + arvif = rcu_dereference(ahsta->ahvif->link[link_id]); + + if (arsta_p == arsta) + continue; + + if (!arvif->is_started) + continue; + + ml->partner_info[i].vdev_id = arvif->vdev_id; + ml->partner_info[i].hw_link_id = arvif->ar->pdev->hw_link_id; + ml->partner_info[i].assoc_link = arsta_p->is_assoc_link; + ml->partner_info[i].primary_umac = arsta_p->is_assoc_link; + ml->partner_info[i].logical_link_idx_valid = true; + ml->partner_info[i].logical_link_idx = arsta_p->link_idx; + ml->num_partner_links++; + + i++; + } + + rcu_read_unlock(); } static void ath12k_peer_assoc_prepare(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, struct ath12k_wmi_peer_assoc_arg *arg, bool reassoc) { - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); memset(arg, 0, sizeof(*arg)); reinit_completion(&ar->peer_assoc_done); arg->peer_new_assoc = !reassoc; - ath12k_peer_assoc_h_basic(ar, vif, sta, arg); - ath12k_peer_assoc_h_crypto(ar, vif, sta, arg); - ath12k_peer_assoc_h_rates(ar, vif, sta, arg); - ath12k_peer_assoc_h_ht(ar, vif, sta, arg); - ath12k_peer_assoc_h_vht(ar, vif, sta, arg); - ath12k_peer_assoc_h_he(ar, vif, sta, arg); - ath12k_peer_assoc_h_eht(ar, vif, sta, arg); - ath12k_peer_assoc_h_qos(ar, vif, sta, arg); - ath12k_peer_assoc_h_phymode(ar, vif, sta, arg); - ath12k_peer_assoc_h_smps(sta, arg); - + ath12k_peer_assoc_h_basic(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_crypto(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_rates(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_ht(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_vht(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_he(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_he_6ghz(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_eht(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_qos(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_phymode(ar, arvif, arsta, arg); + ath12k_peer_assoc_h_smps(arsta, arg); + ath12k_peer_assoc_h_mlo(arsta, arg); + + arsta->peer_nss = arg->peer_nss; /* TODO: amsdu_disable req? */ } -static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_vif *arvif, +static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_link_vif *arvif, const u8 *addr, - const struct ieee80211_sta_ht_cap *ht_cap) + const struct ieee80211_sta_ht_cap *ht_cap, + const struct ieee80211_he_6ghz_capa *he_6ghz_capa) { - int smps; + int smps, ret = 0; - if (!ht_cap->ht_supported) + if (!ht_cap->ht_supported && !he_6ghz_capa) return 0; - smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; - smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; - - if (smps >= ARRAY_SIZE(ath12k_smps_map)) - return -EINVAL; + ret = ath12k_get_smps_from_capa(ht_cap, he_6ghz_capa, &smps); + if (ret < 0) + return ret; return ath12k_wmi_set_peer_param(ar, addr, arvif->vdev_id, WMI_PEER_MIMO_PS_STATE, ath12k_smps_map[smps]); } +static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, + struct ieee80211_link_sta *link_sta) +{ + u32 bw; + + switch (link_sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + bw = WMI_PEER_CHWIDTH_160MHZ; + break; + case IEEE80211_STA_RX_BW_320: + bw = WMI_PEER_CHWIDTH_320MHZ; + break; + default: + ath12k_warn(ar->ab, "Invalid bandwidth %d for link station %pM\n", + link_sta->bandwidth, link_sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + return bw; +} + static void ath12k_bss_assoc(struct ath12k *ar, - struct ath12k_vif *arvif, + struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *bss_conf) { - struct ieee80211_vif *vif = arvif->vif; - struct ath12k_wmi_peer_assoc_arg peer_arg; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ath12k_wmi_vdev_up_params params = {}; + struct ieee80211_link_sta *link_sta; + u8 link_id = bss_conf->link_id; + struct ath12k_link_sta *arsta; struct ieee80211_sta *ap_sta; + struct ath12k_sta *ahsta; struct ath12k_peer *peer; bool is_auth = false; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + struct ath12k_wmi_peer_assoc_arg *peer_arg __free(kfree) = + kzalloc(sizeof(*peer_arg), GFP_KERNEL); + if (!peer_arg) + return; - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n", - arvif->vdev_id, arvif->bssid, arvif->aid); + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "mac vdev %i link id %u assoc bssid %pM aid %d\n", + arvif->vdev_id, link_id, arvif->bssid, ahvif->aid); rcu_read_lock(); - ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); + /* During ML connection, cfg.ap_addr has the MLD address. For + * non-ML connection, it has the BSSID. + */ + ap_sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); if (!ap_sta) { ath12k_warn(ar->ab, "failed to find station entry for bss %pM vdev %i\n", - bss_conf->bssid, arvif->vdev_id); + vif->cfg.ap_addr, arvif->vdev_id); + rcu_read_unlock(); + return; + } + + ahsta = ath12k_sta_to_ahsta(ap_sta); + + arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + ahsta->link[link_id]); + if (WARN_ON(!arsta)) { + rcu_read_unlock(); + return; + } + + link_sta = ath12k_mac_get_link_sta(arsta); + if (WARN_ON(!link_sta)) { rcu_read_unlock(); return; } - ath12k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg, false); + ath12k_peer_assoc_prepare(ar, arvif, arsta, peer_arg, false); rcu_read_unlock(); - ret = ath12k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (ret) { ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", bss_conf->bssid, arvif->vdev_id, ret); @@ -2450,7 +3246,7 @@ static void ath12k_bss_assoc(struct ath12k *ar, } ret = ath12k_setup_peer_smps(ar, arvif, bss_conf->bssid, - &ap_sta->deflink.ht_cap); + &link_sta->ht_cap, &link_sta->he_6ghz_capa); if (ret) { ath12k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", arvif->vdev_id, ret); @@ -2459,10 +3255,13 @@ static void ath12k_bss_assoc(struct ath12k *ar, WARN_ON(arvif->is_up); - arvif->aid = vif->cfg.aid; + ahvif->aid = vif->cfg.aid; ether_addr_copy(arvif->bssid, bss_conf->bssid); - ret = ath12k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); + params.vdev_id = arvif->vdev_id; + params.aid = ahvif->aid; + params.bssid = arvif->bssid; + ret = ath12k_wmi_vdev_up(ar, ¶ms); if (ret) { ath12k_warn(ar->ab, "failed to set vdev %d up: %d\n", arvif->vdev_id, ret); @@ -2470,6 +3269,7 @@ static void ath12k_bss_assoc(struct ath12k *ar, } arvif->is_up = true; + arvif->rekey_data.enable_offload = false; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n", @@ -2498,14 +3298,19 @@ static void ath12k_bss_assoc(struct ath12k *ar, if (ret) ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", arvif->vdev_id, ret); + + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) + ath12k_mac_11d_scan_stop_all(ar->ab); } static void ath12k_bss_disassoc(struct ath12k *ar, - struct ath12k_vif *arvif) + struct ath12k_link_vif *arvif) { int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n", arvif->vdev_id, arvif->bssid); @@ -2517,7 +3322,9 @@ static void ath12k_bss_disassoc(struct ath12k *ar, arvif->is_up = false; - /* TODO: cancel connection_loss_work */ + memset(&arvif->rekey_data, 0, sizeof(arvif->rekey_data)); + + cancel_delayed_work(&arvif->connection_loss_work); } static u32 ath12k_mac_get_rate_hw_value(int bitrate) @@ -2546,22 +3353,33 @@ static u32 ath12k_mac_get_rate_hw_value(int bitrate) } static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, - struct ieee80211_vif *vif, + struct ath12k_link_vif *arvif, struct cfg80211_chan_def *def) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); const struct ieee80211_supported_band *sband; + struct ieee80211_bss_conf *bss_conf; u8 basic_rate_idx; int hw_rate_code; u32 vdev_param; u16 bitrate; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); + + bss_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!bss_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in mgmt rate calc for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } sband = hw->wiphy->bands[def->chan->band]; - basic_rate_idx = ffs(vif->bss_conf.basic_rates) - 1; + if (bss_conf->basic_rates) + basic_rate_idx = __ffs(bss_conf->basic_rates); + else + basic_rate_idx = 0; bitrate = sband->bitrates[basic_rate_idx].bitrate; hw_rate_code = ath12k_mac_get_rate_hw_value(bitrate); @@ -2583,9 +3401,188 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret); } -static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, +static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif, + struct ath12k_link_vif *arvif, int link_id) +{ + struct ath12k_hw *ah = ahvif->ah; + u8 _link_id; + int i; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (WARN_ON(!arvif)) + return; + + if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS)) + return; + + if (link_id < 0) + _link_id = 0; + else + _link_id = link_id; + + arvif->ahvif = ahvif; + arvif->link_id = _link_id; + + /* Protects the datapath stats update on a per link basis */ + spin_lock_init(&arvif->link_stats_lock); + + INIT_LIST_HEAD(&arvif->list); + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath12k_mac_vif_sta_connection_loss_work); + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + + /* Handle MLO related assignments */ + if (link_id >= 0) { + rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); + ahvif->links_map |= BIT(_link_id); + } + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac init link arvif (link_id %d%s) for vif %pM. links_map 0x%x", + _link_id, (link_id < 0) ? " deflink" : "", ahvif->vif->addr, + ahvif->links_map); +} + +static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw, + struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar = arvif->ar; + int ret; + + lockdep_assert_wiphy(ah->hw->wiphy); + + cancel_delayed_work_sync(&arvif->connection_loss_work); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)", + arvif->vdev_id, arvif->link_id); + + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) + ath12k_mac_11d_scan_stop(ar); + + if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); + if (ret) + ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d", + arvif->vdev_id, arvif->link_id, ret); + } + ath12k_mac_vdev_delete(ar, arvif); +} + +static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, + struct ieee80211_vif *vif, + u8 link_id) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + + lockdep_assert_wiphy(ah->hw->wiphy); + + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + if (arvif) + return arvif; + + /* If this is the first link arvif being created for an ML VIF + * use the preallocated deflink memory except for scan arvifs + */ + if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) { + arvif = &ahvif->deflink; + + if (vif->type == NL80211_IFTYPE_STATION) + arvif->is_sta_assoc_link = true; + } else { + arvif = kzalloc(sizeof(*arvif), GFP_KERNEL); + if (!arvif) + return NULL; + } + + ath12k_mac_init_arvif(ahvif, arvif, link_id); + + return arvif; +} + +static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = ahvif->ah; + + lockdep_assert_wiphy(ah->hw->wiphy); + + rcu_assign_pointer(ahvif->link[arvif->link_id], NULL); + synchronize_rcu(); + ahvif->links_map &= ~BIT(arvif->link_id); + + if (arvif != &ahvif->deflink) + kfree(arvif); + else + memset(arvif, 0, sizeof(*arvif)); +} + +static int +ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + unsigned long to_remove = old_links & ~new_links; + unsigned long to_add = ~old_links & new_links; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_link_vif *arvif; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac vif link changed for MLD %pM old_links 0x%x new_links 0x%x\n", + vif->addr, old_links, new_links); + + for_each_set_bit(link_id, &to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + /* mac80211 wants to add link but driver already has the + * link. This should not happen ideally. + */ + if (WARN_ON(arvif)) + return -EINVAL; + + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + if (WARN_ON(!arvif)) + return -EINVAL; + } + + for_each_set_bit(link_id, &to_remove, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (WARN_ON(!arvif)) + return -EINVAL; + + if (!arvif->is_created) + continue; + + if (WARN_ON(!arvif->ar)) + return -EINVAL; + + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } + + return 0; +} + +static int ath12k_mac_fils_discovery(struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *info) { + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); struct ath12k *ar = arvif->ar; struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct sk_buff *tmpl; @@ -2596,7 +3593,7 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, if (info->fils_discovery.max_interval) { interval = info->fils_discovery.max_interval; - tmpl = ieee80211_get_fils_discovery_tmpl(hw, arvif->vif); + tmpl = ieee80211_get_fils_discovery_tmpl(hw, vif); if (tmpl) ret = ath12k_wmi_fils_discovery_tmpl(ar, arvif->vdev_id, tmpl); @@ -2604,8 +3601,7 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, unsol_bcast_probe_resp_enabled = 1; interval = info->unsol_bcast_probe_resp_interval; - tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, - arvif->vif); + tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); if (tmpl) ret = ath12k_wmi_probe_resp_tmpl(ar, arvif->vdev_id, tmpl); @@ -2630,31 +3626,110 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, return ret; } -static void ath12k_mac_vif_setup_ps(struct ath12k_vif *arvif) +static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 changed) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + unsigned long links = ahvif->links_map; + struct ieee80211_bss_conf *info; + struct ath12k_link_vif *arvif; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k *ar; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + if (changed & BSS_CHANGED_SSID && vif->type == NL80211_IFTYPE_AP) { + ahvif->u.ap.ssid_len = vif->cfg.ssid_len; + if (vif->cfg.ssid_len) + memcpy(ahvif->u.ap.ssid, vif->cfg.ssid, vif->cfg.ssid_len); + } + + if (changed & BSS_CHANGED_ASSOC) { + if (vif->cfg.assoc) { + /* only in station mode we can get here, so it's safe + * to use ap_addr + */ + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); + if (!sta) { + rcu_read_unlock(); + WARN_ONCE(1, "failed to find sta with addr %pM\n", + vif->cfg.ap_addr); + return; + } + + ahsta = ath12k_sta_to_ahsta(sta); + arvif = wiphy_dereference(hw->wiphy, + ahvif->link[ahsta->assoc_link_id]); + rcu_read_unlock(); + + ar = arvif->ar; + /* there is no reason for which an assoc link's + * bss info does not exist + */ + info = ath12k_mac_get_link_bss_conf(arvif); + ath12k_bss_assoc(ar, arvif, info); + + /* exclude assoc link as it is done above */ + links &= ~BIT(ahsta->assoc_link_id); + } + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (!arvif || !arvif->ar) + continue; + + ar = arvif->ar; + + if (vif->cfg.assoc) { + info = ath12k_mac_get_link_bss_conf(arvif); + if (!info) + continue; + + ath12k_bss_assoc(ar, arvif, info); + } else { + ath12k_bss_disassoc(ar, arvif); + } + } + } +} + +static void ath12k_mac_vif_setup_ps(struct ath12k_link_vif *arvif) { struct ath12k *ar = arvif->ar; - struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_vif *vif = arvif->ahvif->vif; struct ieee80211_conf *conf = &ath12k_ar_to_hw(ar)->conf; enum wmi_sta_powersave_param param; + struct ieee80211_bss_conf *info; enum wmi_sta_ps_mode psmode; int ret; int timeout; bool enable_ps; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (vif->type != NL80211_IFTYPE_STATION) return; - enable_ps = arvif->ps; + enable_ps = arvif->ahvif->ps; if (enable_ps) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; timeout = conf->dynamic_ps_timeout; if (timeout == 0) { + info = ath12k_mac_get_link_bss_conf(arvif); + if (!info) { + ath12k_warn(ar->ab, "unable to access bss link conf in setup ps for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } + /* firmware doesn't like 0 */ - timeout = ieee80211_tu_to_usec(vif->bss_conf.beacon_int) / 1000; + timeout = ieee80211_tu_to_usec(info->beacon_int) / 1000; } ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, @@ -2677,12 +3752,25 @@ static void ath12k_mac_vif_setup_ps(struct ath12k_vif *arvif) psmode, arvif->vdev_id, ret); } +static bool ath12k_mac_supports_station_tpc(struct ath12k *ar, + struct ath12k_vif *ahvif, + const struct cfg80211_chan_def *chandef) +{ + return ath12k_wmi_supports_6ghz_cc_ext(ar) && + test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE && + chandef->chan && + chandef->chan->band == NL80211_BAND_6GHZ; +} + static void ath12k_mac_bss_info_changed(struct ath12k *ar, - struct ath12k_vif *arvif, + struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *info, u64 changed) { - struct ieee80211_vif *vif = arvif->vif; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); struct ieee80211_vif_cfg *vif_cfg = &vif->cfg; struct cfg80211_chan_def def; u32 param_id, param_value; @@ -2696,7 +3784,7 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, u8 rateidx; u32 rate; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (changed & BSS_CHANGED_BEACON_INT) { arvif->beacon_interval = info->beacon_int; @@ -2752,10 +3840,10 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, if (changed & BSS_CHANGED_SSID && vif->type == NL80211_IFTYPE_AP) { - arvif->u.ap.ssid_len = vif->cfg.ssid_len; + ahvif->u.ap.ssid_len = vif->cfg.ssid_len; if (vif->cfg.ssid_len) - memcpy(arvif->u.ap.ssid, vif->cfg.ssid, vif->cfg.ssid_len); - arvif->u.ap.hidden_ssid = info->hidden_ssid; + memcpy(ahvif->u.ap.ssid, vif->cfg.ssid, vif->cfg.ssid_len); + ahvif->u.ap.hidden_ssid = info->hidden_ssid; } if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) @@ -2764,8 +3852,8 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, if (changed & BSS_CHANGED_BEACON_ENABLED) { ath12k_control_beaconing(arvif, info); - if (arvif->is_up && vif->bss_conf.he_support && - vif->bss_conf.he_oper.params) { + if (arvif->is_up && info->he_support && + info->he_oper.params) { /* TODO: Extend to support 1024 BA Bitmap size */ ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, WMI_VDEV_PARAM_BA_MODE, @@ -2776,7 +3864,7 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, arvif->vdev_id); param_id = WMI_VDEV_PARAM_HEOPS_0_31; - param_value = vif->bss_conf.he_oper.params; + param_value = info->he_oper.params; ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param_id, param_value); ath12k_dbg(ar->ab, ATH12K_DBG_MAC, @@ -2866,16 +3954,20 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, } if (changed & BSS_CHANGED_MCAST_RATE && - !ath12k_mac_vif_chan(arvif->vif, &def)) { + !ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)) { band = def.chan->band; - mcast_rate = vif->bss_conf.mcast_rate[band]; + mcast_rate = info->mcast_rate[band]; - if (mcast_rate > 0) + if (mcast_rate > 0) { rateidx = mcast_rate - 1; - else - rateidx = ffs(vif->bss_conf.basic_rates) - 1; + } else { + if (info->basic_rates) + rateidx = __ffs(info->basic_rates); + else + rateidx = 0; + } - if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) + if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) rateidx += ATH12K_MAC_FIRST_OFDM_RATE_IDX; bitrate = ath12k_legacy_rates[rateidx].bitrate; @@ -2910,8 +4002,8 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, } if (changed & BSS_CHANGED_BASIC_RATES && - !ath12k_mac_vif_chan(arvif->vif, &def)) - ath12k_recalculate_mgmt_rate(ar, vif, &def); + !ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)) + ath12k_recalculate_mgmt_rate(ar, arvif, &def); if (changed & BSS_CHANGED_TWT) { if (info->twt_requester || info->twt_responder) @@ -2956,27 +4048,113 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, if (changed & BSS_CHANGED_PS && ar->ab->hw_params->supports_sta_ps) { - arvif->ps = vif_cfg->ps; + ahvif->ps = vif_cfg->ps; ath12k_mac_vif_setup_ps(arvif); } } -static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u64 changed) +static struct ath12k_vif_cache *ath12k_ahvif_get_link_cache(struct ath12k_vif *ahvif, + u8 link_id) +{ + if (!ahvif->cache[link_id]) { + ahvif->cache[link_id] = kzalloc(sizeof(*ahvif->cache[0]), GFP_KERNEL); + if (ahvif->cache[link_id]) + INIT_LIST_HEAD(&ahvif->cache[link_id]->key_conf.list); + } + + return ahvif->cache[link_id]; +} + +static void ath12k_ahvif_put_link_key_cache(struct ath12k_vif_cache *cache) +{ + struct ath12k_key_conf *key_conf, *tmp; + + if (!cache || list_empty(&cache->key_conf.list)) + return; + list_for_each_entry_safe(key_conf, tmp, &cache->key_conf.list, list) { + list_del(&key_conf->list); + kfree(key_conf); + } +} + +static void ath12k_ahvif_put_link_cache(struct ath12k_vif *ahvif, u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return; + + ath12k_ahvif_put_link_key_cache(ahvif->cache[link_id]); + kfree(ahvif->cache[link_id]); + ahvif->cache[link_id] = NULL; +} + +static void ath12k_mac_op_link_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_vif_cache *cache; + struct ath12k_link_vif *arvif; + u8 link_id = info->link_id; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + + /* if the vdev is not created on a certain radio, + * cache the info to be updated later on vdev creation + */ + + if (!arvif || !arvif->is_created) { + cache = ath12k_ahvif_get_link_cache(ahvif, link_id); + if (!cache) + return; + + cache->bss_conf_changed |= changed; + + return; + } + + ar = arvif->ar; ath12k_mac_bss_info_changed(ar, arvif, info, changed); +} + +static struct ath12k* +ath12k_mac_select_scan_device(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 center_freq) +{ + struct ath12k_hw *ah = hw->priv; + enum nl80211_band band; + struct ath12k *ar; + int i; + + if (ah->num_radio == 1) + return ah->radio; + + /* Currently mac80211 supports splitting scan requests into + * multiple scan requests per band. + * Loop through first channel and determine the scan radio + * TODO: There could be 5 GHz low/high channels in that case + * split the hw request and perform multiple scans + */ + + if (center_freq < ATH12K_MIN_5GHZ_FREQ) + band = NL80211_BAND_2GHZ; + else if (center_freq < ATH12K_MIN_6GHZ_FREQ) + band = NL80211_BAND_5GHZ; + else + band = NL80211_BAND_6GHZ; + + for_each_ar(ah, ar, i) { + /* TODO 5 GHz low high split changes */ + if (ar->mac.sbands[band].channels) + return ar; + } - mutex_unlock(&ar->conf_mutex); + return NULL; } void __ath12k_mac_scan_finish(struct ath12k *ar) @@ -2994,22 +4172,9 @@ void __ath12k_mac_scan_finish(struct ath12k *ar) ieee80211_remain_on_channel_expired(hw); fallthrough; case ATH12K_SCAN_STARTING: - if (!ar->scan.is_roc) { - struct cfg80211_scan_info info = { - .aborted = ((ar->scan.state == - ATH12K_SCAN_ABORTING) || - (ar->scan.state == - ATH12K_SCAN_STARTING)), - }; - - ieee80211_scan_completed(hw, &info); - } - - ar->scan.state = ATH12K_SCAN_IDLE; - ar->scan_channel = NULL; - ar->scan.roc_freq = 0; cancel_delayed_work(&ar->scan.timeout); - complete(&ar->scan.completed); + complete_all(&ar->scan.completed); + wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk); break; } } @@ -3029,7 +4194,7 @@ static int ath12k_scan_stop(struct ath12k *ar) }; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); /* TODO: Fill other STOP Params */ arg.pdev_id = ar->pdev->pdev_id; @@ -3050,15 +4215,15 @@ static int ath12k_scan_stop(struct ath12k *ar) } out: - /* Scan state should be updated upon scan completion but in case - * firmware fails to deliver the event (for whatever reason) it is - * desired to clean up scan state anyway. Firmware may have just - * dropped the scan completion event delivery due to transport pipe - * being overflown with data and/or it can recover on its own before - * next scan request is submitted. + /* Scan state should be updated in scan completion worker but in + * case firmware fails to deliver the event (for whatever reason) + * it is desired to clean up scan state anyway. Firmware may have + * just dropped the scan completion event delivery due to transport + * pipe being overflown with data and/or it can recover on its own + * before next scan request is submitted. */ spin_lock_bh(&ar->data_lock); - if (ar->scan.state != ATH12K_SCAN_IDLE) + if (ret) __ath12k_mac_scan_finish(ar); spin_unlock_bh(&ar->data_lock); @@ -3069,7 +4234,7 @@ static void ath12k_scan_abort(struct ath12k *ar) { int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); spin_lock_bh(&ar->data_lock); @@ -3104,9 +4269,56 @@ static void ath12k_scan_timeout_work(struct work_struct *work) struct ath12k *ar = container_of(work, struct ath12k, scan.timeout.work); - mutex_lock(&ar->conf_mutex); + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); ath12k_scan_abort(ar); - mutex_unlock(&ar->conf_mutex); + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); +} + +static void ath12k_scan_vdev_clean_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct ath12k *ar = container_of(work, struct ath12k, + scan.vdev_clean_wk); + struct ath12k_hw *ah = ar->ah; + struct ath12k_link_vif *arvif; + + lockdep_assert_wiphy(wiphy); + + arvif = ar->scan.arvif; + + /* The scan vdev has already been deleted. This can occur when a + * new scan request is made on the same vif with a different + * frequency, causing the scan arvif to move from one radio to + * another. Or, scan was abrupted and via remove interface, the + * arvif is already deleted. Alternatively, if the scan vdev is not + * being used as an actual vdev, then do not delete it. + */ + if (!arvif || arvif->is_started) + goto work_complete; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac clean scan vdev (link id %u)", + arvif->link_id); + + ath12k_mac_remove_link_interface(ah->hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + +work_complete: + spin_lock_bh(&ar->data_lock); + ar->scan.arvif = NULL; + if (!ar->scan.is_roc) { + struct cfg80211_scan_info info = { + .aborted = ((ar->scan.state == + ATH12K_SCAN_ABORTING) || + (ar->scan.state == + ATH12K_SCAN_STARTING)), + }; + + ieee80211_scan_completed(ar->ah->hw, &info); + } + + ar->scan.state = ATH12K_SCAN_IDLE; + ar->scan_channel = NULL; + ar->scan.roc_freq = 0; + spin_unlock_bh(&ar->data_lock); } static int ath12k_start_scan(struct ath12k *ar, @@ -3114,7 +4326,7 @@ static int ath12k_start_scan(struct ath12k *ar, { int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); ret = ath12k_wmi_send_scan_start_cmd(ar, arg); if (ret) @@ -3143,21 +4355,231 @@ static int ath12k_start_scan(struct ath12k *ar, return 0; } +int ath12k_mac_get_fw_stats(struct ath12k *ar, + struct ath12k_fw_stats_req_params *param) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + unsigned long time_left; + int ret; + + guard(mutex)(&ah->hw_mutex); + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + ath12k_fw_stats_reset(ar); + + reinit_completion(&ar->fw_stats_complete); + reinit_completion(&ar->fw_stats_done); + + ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id, + param->vdev_id, param->pdev_id); + if (ret) { + ath12k_warn(ab, "failed to request fw stats: %d\n", ret); + return ret; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "get fw stat pdev id %d vdev id %d stats id 0x%x\n", + param->pdev_id, param->vdev_id, param->stats_id); + + time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); + if (!time_left) { + ath12k_warn(ab, "time out while waiting for get fw stats\n"); + return -ETIMEDOUT; + } + + /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back + * when stats data buffer limit is reached. fw_stats_complete + * is completed once host receives first event from firmware, but + * still there could be more events following. Below is to wait + * until firmware completes sending all the events. + */ + time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ); + if (!time_left) { + ath12k_warn(ab, "time out while waiting for fw stats done\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, + int *dbm) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_fw_stats_req_params params = {}; + struct ath12k_fw_stats_pdev *pdev; + struct ath12k_hw *ah = hw->priv; + struct ath12k_link_vif *arvif; + struct ath12k_base *ab; + struct ath12k *ar; + int ret; + + /* Final Tx power is minimum of Target Power, CTL power, Regulatory + * Power, PSD EIRP Power. We just know the Regulatory power from the + * regulatory rules obtained. FW knows all these power and sets the min + * of these. Hence, we request the FW pdev stats in which FW reports + * the minimum of all vdev's channel Tx power. + */ + lockdep_assert_wiphy(hw->wiphy); + + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + if (!arvif || !arvif->ar) + return -EINVAL; + + ar = arvif->ar; + ab = ar->ab; + if (ah->state != ATH12K_HW_STATE_ON) + goto err_fallback; + + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) + return -EAGAIN; + + /* Limit the requests to Firmware for fetching the tx power */ + if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID && + time_before(jiffies, + msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) + + ar->last_tx_power_update)) + goto send_tx_power; + + params.pdev_id = ar->pdev->pdev_id; + params.vdev_id = arvif->vdev_id; + params.stats_id = WMI_REQUEST_PDEV_STAT; + ret = ath12k_mac_get_fw_stats(ar, ¶ms); + if (ret) { + ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + goto err_fallback; + } + + spin_lock_bh(&ar->data_lock); + pdev = list_first_entry_or_null(&ar->fw_stats.pdevs, + struct ath12k_fw_stats_pdev, list); + if (!pdev) { + spin_unlock_bh(&ar->data_lock); + goto err_fallback; + } + + /* tx power reported by firmware is in units of 0.5 dBm */ + ar->chan_tx_pwr = pdev->chan_tx_power / 2; + spin_unlock_bh(&ar->data_lock); + ar->last_tx_power_update = jiffies; + +send_tx_power: + *dbm = ar->chan_tx_pwr; + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower fetched from firmware %d dBm\n", + *dbm); + return 0; + +err_fallback: + /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */ + *dbm = vif->bss_conf.txpower; + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n", + *dbm); + return 0; +} + +static u8 +ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar) +{ + struct ath12k_link_vif *arvif; + struct ath12k_hw *ah = ahvif->ah; + unsigned long links = ahvif->links_map; + u8 link_id; + + lockdep_assert_wiphy(ah->hw->wiphy); + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + + if (!arvif || !arvif->is_created) + continue; + + if (ar == arvif->ar) + return link_id; + } + + /* input ar is not assigned to any of the links of ML VIF, use scan + * link (15) for scan vdev creation. + */ + return ATH12K_DEFAULT_SCAN_LINK; +} + static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; struct cfg80211_scan_request *req = &hw_req->req; - struct ath12k_wmi_scan_req_arg arg = {}; + struct ath12k_wmi_scan_req_arg *arg = NULL; + u8 link_id; int ret; int i; + bool create = true; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); + arvif = &ahvif->deflink; + + /* Since the targeted scan device could depend on the frequency + * requested in the hw_req, select the corresponding radio + */ + ar = ath12k_mac_select_scan_device(hw, vif, hw_req->req.channels[0]->center_freq); + if (!ar) + return -EINVAL; + + /* check if any of the links of ML VIF is already started on + * radio(ar) corresponding to given scan frequency and use it, + * if not use scan link (link 15) for scan purpose. + */ + link_id = ath12k_mac_find_link_id_by_ar(ahvif, ar); + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac link ID %d selected for scan", + arvif->link_id); + + /* If the vif is already assigned to a specific vdev of an ar, + * check whether its already started, vdev which is started + * are not allowed to switch to a new radio. + * If the vdev is not started, but was earlier created on a + * different ar, delete that vdev and create a new one. We don't + * delete at the scan stop as an optimization to avoid redundant + * delete-create vdev's for the same ar, in case the request is + * always on the same band for the vif + */ + if (arvif->is_created) { + if (WARN_ON(!arvif->ar)) + return -EINVAL; + + if (ar != arvif->ar && arvif->is_started) + return -EINVAL; + + if (ar != arvif->ar) { + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } else { + create = false; + } + } + + if (create) { + /* Previous arvif would've been cleared in radio switch block + * above, assign arvif again for create. + */ + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + + ret = ath12k_mac_vdev_create(ar, arvif); + if (ret) { + ath12k_warn(ar->ab, "unable to create scan vdev %d\n", ret); + return -EINVAL; + } + } spin_lock_bh(&ar->data_lock); switch (ar->scan.state) { @@ -3166,7 +4588,7 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, reinit_completion(&ar->scan.completed); ar->scan.state = ATH12K_SCAN_STARTING; ar->scan.is_roc = false; - ar->scan.vdev_id = arvif->vdev_id; + ar->scan.arvif = arvif; ret = 0; break; case ATH12K_SCAN_STARTING: @@ -3180,61 +4602,85 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - ath12k_wmi_start_scan_init(ar, &arg); - arg.vdev_id = arvif->vdev_id; - arg.scan_id = ATH12K_SCAN_ID; + arg = kzalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) { + ret = -ENOMEM; + goto exit; + } + + ath12k_wmi_start_scan_init(ar, arg); + arg->vdev_id = arvif->vdev_id; + arg->scan_id = ATH12K_SCAN_ID; if (req->ie_len) { - arg.extraie.ptr = kmemdup(req->ie, req->ie_len, GFP_KERNEL); - if (!arg.extraie.ptr) { + arg->extraie.ptr = kmemdup(req->ie, req->ie_len, GFP_KERNEL); + if (!arg->extraie.ptr) { ret = -ENOMEM; goto exit; } - arg.extraie.len = req->ie_len; + arg->extraie.len = req->ie_len; } if (req->n_ssids) { - arg.num_ssids = req->n_ssids; - for (i = 0; i < arg.num_ssids; i++) - arg.ssid[i] = req->ssids[i]; + arg->num_ssids = req->n_ssids; + for (i = 0; i < arg->num_ssids; i++) + arg->ssid[i] = req->ssids[i]; } else { - arg.scan_f_passive = 1; + arg->scan_f_passive = 1; } if (req->n_channels) { - arg.num_chan = req->n_channels; - arg.chan_list = kcalloc(arg.num_chan, sizeof(*arg.chan_list), - GFP_KERNEL); - - if (!arg.chan_list) { + arg->num_chan = req->n_channels; + arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), + GFP_KERNEL); + if (!arg->chan_list) { ret = -ENOMEM; goto exit; } - for (i = 0; i < arg.num_chan; i++) - arg.chan_list[i] = req->channels[i]->center_freq; + for (i = 0; i < arg->num_chan; i++) + arg->chan_list[i] = req->channels[i]->center_freq; } - ret = ath12k_start_scan(ar, &arg); + ret = ath12k_start_scan(ar, arg); if (ret) { - ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret); + if (ret == -EBUSY) + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "scan engine is busy 11d state %d\n", ar->state_11d); + else + ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret); + spin_lock_bh(&ar->data_lock); ar->scan.state = ATH12K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); + goto exit; } + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac scan started"); + + /* As per cfg80211/mac80211 scan design, it allows only one + * scan at a time. Hence last_scan link id is used for + * tracking the link id on which the scan is been done on + * this vif. + */ + ahvif->last_scan_link = arvif->link_id; + /* Add a margin to account for event/command processing */ ieee80211_queue_delayed_work(ath12k_ar_to_hw(ar), &ar->scan.timeout, - msecs_to_jiffies(arg.max_scan_time + + msecs_to_jiffies(arg->max_scan_time + ATH12K_MAC_SCAN_TIMEOUT_MSECS)); exit: - kfree(arg.chan_list); - - if (req->ie_len) - kfree(arg.extraie.ptr); + if (arg) { + kfree(arg->chan_list); + kfree(arg->extraie.ptr); + kfree(arg); + } - mutex_unlock(&ar->conf_mutex); + if (ar->state_11d == ATH12K_11D_PREPARING && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) + ath12k_mac_11d_scan_start(ar, arvif->vdev_id); return ret; } @@ -3242,19 +4688,25 @@ exit: static void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + u16 link_id = ahvif->last_scan_link; + struct ath12k_link_vif *arvif; struct ath12k *ar; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); + + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (!arvif || arvif->is_started) + return; + + ar = arvif->ar; - mutex_lock(&ar->conf_mutex); ath12k_scan_abort(ar); - mutex_unlock(&ar->conf_mutex); cancel_delayed_work_sync(&ar->scan.timeout); } -static int ath12k_install_key(struct ath12k_vif *arvif, +static int ath12k_install_key(struct ath12k_link_vif *arvif, struct ieee80211_key_conf *key, enum set_key_cmd cmd, const u8 *macaddr, u32 flags) @@ -3269,8 +4721,9 @@ static int ath12k_install_key(struct ath12k_vif *arvif, .key_flags = flags, .macaddr = macaddr, }; + struct ath12k_vif *ahvif = arvif->ahvif; - lockdep_assert_held(&arvif->ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); reinit_completion(&ar->install_key_done); @@ -3287,8 +4740,8 @@ static int ath12k_install_key(struct ath12k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: arg.key_cipher = WMI_CIPHER_AES_CCM; - /* TODO: Re-check if flag is valid */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_TKIP: @@ -3296,12 +4749,10 @@ static int ath12k_install_key(struct ath12k_vif *arvif, arg.key_txmic_len = 8; arg.key_rxmic_len = 8; break; - case WLAN_CIPHER_SUITE_CCMP_256: - arg.key_cipher = WMI_CIPHER_AES_CCM; - break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: arg.key_cipher = WMI_CIPHER_AES_GCM; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; default: ath12k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); @@ -3321,13 +4772,13 @@ install: if (!wait_for_completion_timeout(&ar->install_key_done, 1 * HZ)) return -ETIMEDOUT; - if (ether_addr_equal(macaddr, arvif->vif->addr)) - arvif->key_cipher = key->cipher; + if (ether_addr_equal(macaddr, arvif->bssid)) + ahvif->key_cipher = key->cipher; return ar->install_key_status ? -EINVAL : 0; } -static int ath12k_clear_peer_keys(struct ath12k_vif *arvif, +static int ath12k_clear_peer_keys(struct ath12k_link_vif *arvif, const u8 *addr) { struct ath12k *ar = arvif->ar; @@ -3338,7 +4789,7 @@ static int ath12k_clear_peer_keys(struct ath12k_vif *arvif, int i; u32 flags = 0; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); spin_lock_bh(&ab->base_lock); peer = ath12k_peer_find(ab, arvif->vdev_id, addr); @@ -3369,49 +4820,36 @@ static int ath12k_clear_peer_keys(struct ath12k_vif *arvif, return first_errno; } -static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) +static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, + struct ieee80211_key_conf *key) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar; - struct ath12k_base *ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_sta *sta = NULL; + struct ath12k_base *ab = ar->ab; struct ath12k_peer *peer; - struct ath12k_sta *arsta; + struct ath12k_sta *ahsta; const u8 *peer_addr; - int ret = 0; + int ret; u32 flags = 0; - /* BIP needs to be done in software */ - if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || - key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || - key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 || - key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256) - return 1; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; + if (arsta) + sta = ath12k_ahsta_to_sta(arsta->ahsta); - if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags)) + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) return 1; - if (key->keyidx > WMI_MAX_KEY_INDEX) - return -ENOSPC; - - mutex_lock(&ar->conf_mutex); - if (sta) - peer_addr = sta->addr; - else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) - peer_addr = vif->bss_conf.bssid; + peer_addr = arsta->addr; else - peer_addr = vif->addr; + peer_addr = arvif->bssid; key->hw_key_idx = key->keyidx; /* the peer should not disappear in mid-way (unless FW goes awry) since - * we already hold conf_mutex. we just make sure its there now. + * we already hold wiphy lock. we just make sure its there now. */ spin_lock_bh(&ab->base_lock); peer = ath12k_peer_find(ab, arvif->vdev_id, peer_addr); @@ -3421,14 +4859,13 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (cmd == SET_KEY) { ath12k_warn(ab, "cannot install key for non-existent peer %pM\n", peer_addr); - ret = -EOPNOTSUPP; - goto exit; - } else { - /* if the peer doesn't exist there is no key to disable - * anymore - */ - goto exit; + return -EOPNOTSUPP; } + + /* if the peer doesn't exist there is no key to disable + * anymore + */ + return 0; } if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) @@ -3439,13 +4876,13 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = ath12k_install_key(arvif, key, cmd, peer_addr, flags); if (ret) { ath12k_warn(ab, "ath12k_install_key failed (%d)\n", ret); - goto exit; + return ret; } ret = ath12k_dp_rx_peer_pn_replay_config(arvif, peer_addr, cmd, key); if (ret) { ath12k_warn(ab, "failed to offload PN replay detection %d\n", ret); - goto exit; + return ret; } spin_lock_bh(&ab->base_lock); @@ -3470,7 +4907,7 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ath12k_warn(ab, "peer %pM disappeared!\n", peer_addr); if (sta) { - arsta = ath12k_sta_to_arsta(sta); + ahsta = ath12k_sta_to_ahsta(sta); switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: @@ -3479,21 +4916,152 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: if (cmd == SET_KEY) - arsta->pn_type = HAL_PN_TYPE_WPA; + ahsta->pn_type = HAL_PN_TYPE_WPA; else - arsta->pn_type = HAL_PN_TYPE_NONE; + ahsta->pn_type = HAL_PN_TYPE_NONE; break; default: - arsta->pn_type = HAL_PN_TYPE_NONE; + ahsta->pn_type = HAL_PN_TYPE_NONE; break; } } spin_unlock_bh(&ab->base_lock); -exit: - mutex_unlock(&ar->conf_mutex); - return ret; + return 0; +} + +static int ath12k_mac_update_key_cache(struct ath12k_vif_cache *cache, + enum set_key_cmd cmd, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath12k_key_conf *key_conf, *tmp; + + list_for_each_entry_safe(key_conf, tmp, &cache->key_conf.list, list) { + if (key_conf->key != key) + continue; + + /* If SET key entry is already present in cache, nothing to do, + * just return + */ + if (cmd == SET_KEY) + return 0; + + /* DEL key for an old SET key which driver hasn't flushed yet. + */ + list_del(&key_conf->list); + kfree(key_conf); + } + + if (cmd == SET_KEY) { + key_conf = kzalloc(sizeof(*key_conf), GFP_KERNEL); + + if (!key_conf) + return -ENOMEM; + + key_conf->cmd = cmd; + key_conf->sta = sta; + key_conf->key = key; + list_add_tail(&key_conf->list, + &cache->key_conf.list); + } + + return 0; +} + +static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta = NULL; + struct ath12k_vif_cache *cache; + struct ath12k_sta *ahsta; + unsigned long links; + u8 link_id; + int ret; + + lockdep_assert_wiphy(hw->wiphy); + + /* BIP needs to be done in software */ + if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || + key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || + key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 || + key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256) { + return 1; + } + + if (key->keyidx > WMI_MAX_KEY_INDEX) + return -ENOSPC; + + if (sta) { + ahsta = ath12k_sta_to_ahsta(sta); + + /* For an ML STA Pairwise key is same for all associated link Stations, + * hence do set key for all link STAs which are active. + */ + if (sta->mlo) { + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, + ahvif->link[link_id]); + arsta = wiphy_dereference(hw->wiphy, + ahsta->link[link_id]); + + if (WARN_ON(!arvif || !arsta)) + /* arvif and arsta are expected to be valid when + * STA is present. + */ + continue; + + ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, + arsta, key); + if (ret) + break; + } + + return 0; + } + + arsta = &ahsta->deflink; + arvif = arsta->arvif; + if (WARN_ON(!arvif)) + return -EINVAL; + + ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, arsta, key); + if (ret) + return ret; + + return 0; + } + + if (key->link_id >= 0 && key->link_id < IEEE80211_MLD_MAX_NUM_LINKS) { + link_id = key->link_id; + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + } else { + link_id = 0; + arvif = &ahvif->deflink; + } + + if (!arvif || !arvif->is_created) { + cache = ath12k_ahvif_get_link_cache(ahvif, link_id); + if (!cache) + return -ENOSPC; + + ret = ath12k_mac_update_key_cache(cache, cmd, sta, key); + if (ret) + return ret; + + return 0; + } + + ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, NULL, key); + if (ret) + return ret; + + return 0; } static int @@ -3511,8 +5079,8 @@ ath12k_mac_bitrate_mask_num_vht_rates(struct ath12k *ar, } static int -ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_vif *arvif, - struct ieee80211_sta *sta, +ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, const struct cfg80211_bitrate_mask *mask, enum nl80211_band band) { @@ -3521,7 +5089,7 @@ ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_vif *arvif, u32 rate_code; int ret, i; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); nss = 0; @@ -3534,61 +5102,76 @@ ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_vif *arvif, if (!nss) { ath12k_warn(ar->ab, "No single VHT Fixed rate found to set for %pM", - sta->addr); + arsta->addr); return -EINVAL; } ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Setting Fixed VHT Rate for peer %pM. Device will not switch to any other selected rates", - sta->addr); + arsta->addr); rate_code = ATH12K_HW_RATE_CODE(vht_rate, nss - 1, WMI_RATE_PREAMBLE_VHT); - ret = ath12k_wmi_set_peer_param(ar, sta->addr, + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_PARAM_FIXED_RATE, rate_code); if (ret) ath12k_warn(ar->ab, "failed to update STA %pM Fixed Rate %d: %d\n", - sta->addr, rate_code, ret); + arsta->addr, rate_code, ret); return ret; } -static int ath12k_station_assoc(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - bool reassoc) +static int ath12k_mac_station_assoc(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, + bool reassoc) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k_wmi_peer_assoc_arg peer_arg; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct ieee80211_link_sta *link_sta; int ret; struct cfg80211_chan_def def; enum nl80211_band band; struct cfg80211_bitrate_mask *mask; u8 num_vht_rates; + u8 link_id = arvif->link_id; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return -EPERM; + if (WARN_ON(!rcu_access_pointer(sta->link[link_id]))) + return -EINVAL; + band = def.chan->band; mask = &arvif->bitrate_mask; - ath12k_peer_assoc_prepare(ar, vif, sta, &peer_arg, reassoc); + struct ath12k_wmi_peer_assoc_arg *peer_arg __free(kfree) = + kzalloc(sizeof(*peer_arg), GFP_KERNEL); + if (!peer_arg) + return -ENOMEM; - ret = ath12k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + ath12k_peer_assoc_prepare(ar, arvif, arsta, peer_arg, reassoc); + + if (peer_arg->peer_nss < 1) { + ath12k_warn(ar->ab, + "invalid peer NSS %d\n", peer_arg->peer_nss); + return -EINVAL; + } + ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (ret) { ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", - sta->addr, arvif->vdev_id, ret); + arsta->addr, arvif->vdev_id, ret); return ret; } if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", - sta->addr, arvif->vdev_id); + arsta->addr, arvif->vdev_id); return -ETIMEDOUT; } @@ -3599,8 +5182,19 @@ static int ath12k_station_assoc(struct ath12k *ar, * fixed param. * Note that all other rates and NSS will be disabled for this peer. */ - if (sta->deflink.vht_cap.vht_supported && num_vht_rates == 1) { - ret = ath12k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in station assoc\n"); + return -EINVAL; + } + + spin_lock_bh(&ar->data_lock); + arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta); + arsta->bw_prev = link_sta->bandwidth; + spin_unlock_bh(&ar->data_lock); + + if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) { + ret = ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask, band); if (ret) return ret; @@ -3612,8 +5206,8 @@ static int ath12k_station_assoc(struct ath12k *ar, if (reassoc) return 0; - ret = ath12k_setup_peer_smps(ar, arvif, sta->addr, - &sta->deflink.ht_cap); + ret = ath12k_setup_peer_smps(ar, arvif, arsta->addr, + &link_sta->ht_cap, &link_sta->he_6ghz_capa); if (ret) { ath12k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", arvif->vdev_id, ret); @@ -3628,10 +5222,10 @@ static int ath12k_station_assoc(struct ath12k *ar, } if (sta->wme && sta->uapsd_queues) { - ret = ath12k_peer_assoc_qos_ap(ar, arvif, sta); + ret = ath12k_peer_assoc_qos_ap(ar, arvif, arsta); if (ret) { ath12k_warn(ar->ab, "failed to set qos params for STA %pM for vdev %i: %d\n", - sta->addr, arvif->vdev_id, ret); + arsta->addr, arvif->vdev_id, ret); return ret; } } @@ -3639,36 +5233,27 @@ static int ath12k_station_assoc(struct ath12k *ar, return 0; } -static int ath12k_station_disassoc(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +static int ath12k_mac_station_disassoc(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - int ret; + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (!sta->wme) { arvif->num_legacy_stations--; - ret = ath12k_recalc_rtscts_prot(arvif); - if (ret) - return ret; + return ath12k_recalc_rtscts_prot(arvif); } - ret = ath12k_clear_peer_keys(arvif, sta->addr); - if (ret) { - ath12k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", - arvif->vdev_id, ret); - return ret; - } return 0; } -static void ath12k_sta_rc_update_wk(struct work_struct *wk) +static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk) { + struct ieee80211_link_sta *link_sta; struct ath12k *ar; - struct ath12k_vif *arvif; - struct ath12k_sta *arsta; + struct ath12k_link_vif *arvif; struct ieee80211_sta *sta; struct cfg80211_chan_def def; enum nl80211_band band; @@ -3677,15 +5262,19 @@ static void ath12k_sta_rc_update_wk(struct work_struct *wk) u32 changed, bw, nss, smps, bw_prev; int err, num_vht_rates; const struct cfg80211_bitrate_mask *mask; - struct ath12k_wmi_peer_assoc_arg peer_arg; enum wmi_phy_mode peer_phymode; + struct ath12k_link_sta *arsta; + struct ieee80211_vif *vif; + + lockdep_assert_wiphy(wiphy); - arsta = container_of(wk, struct ath12k_sta, update_wk); - sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); + arsta = container_of(wk, struct ath12k_link_sta, update_wk); + sta = ath12k_ahsta_to_sta(arsta->ahsta); arvif = arsta->arvif; + vif = ath12k_ahvif_to_vif(arvif->ahvif); ar = arvif->ar; - if (WARN_ON(ath12k_mac_vif_chan(arvif->vif, &def))) + if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return; band = def.chan->band; @@ -3704,15 +5293,18 @@ static void ath12k_sta_rc_update_wk(struct work_struct *wk) spin_unlock_bh(&ar->data_lock); - mutex_lock(&ar->conf_mutex); - nss = max_t(u32, 1, nss); nss = min(nss, max(ath12k_mac_max_ht_nss(ht_mcs_mask), ath12k_mac_max_vht_nss(vht_mcs_mask))); + struct ath12k_wmi_peer_assoc_arg *peer_arg __free(kfree) = + kzalloc(sizeof(*peer_arg), GFP_KERNEL); + if (!peer_arg) + return; + if (changed & IEEE80211_RC_BW_CHANGED) { - ath12k_peer_assoc_h_phymode(ar, arvif->vif, sta, &peer_arg); - peer_phymode = peer_arg.peer_phymode; + ath12k_peer_assoc_h_phymode(ar, arvif, arsta, peer_arg); + peer_phymode = peer_arg->peer_phymode; if (bw > bw_prev) { /* Phymode shows maximum supported channel width, if we @@ -3721,65 +5313,65 @@ static void ath12k_sta_rc_update_wk(struct work_struct *wk) * WMI_PEER_CHWIDTH */ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac bandwidth upgrade for sta %pM new %d old %d\n", - sta->addr, bw, bw_prev); - err = ath12k_wmi_set_peer_param(ar, sta->addr, + arsta->addr, bw, bw_prev); + err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_PHYMODE, peer_phymode); if (err) { ath12k_warn(ar->ab, "failed to update STA %pM to peer phymode %d: %d\n", - sta->addr, peer_phymode, err); - goto err_rc_bw_changed; + arsta->addr, peer_phymode, err); + return; } - err = ath12k_wmi_set_peer_param(ar, sta->addr, + err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_CHWIDTH, bw); if (err) ath12k_warn(ar->ab, "failed to update STA %pM to peer bandwidth %d: %d\n", - sta->addr, bw, err); + arsta->addr, bw, err); } else { /* When we downgrade bandwidth this will conflict with phymode * and cause to trigger firmware crash. In this case we send * WMI_PEER_CHWIDTH followed by WMI_PEER_PHYMODE */ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac bandwidth downgrade for sta %pM new %d old %d\n", - sta->addr, bw, bw_prev); - err = ath12k_wmi_set_peer_param(ar, sta->addr, + arsta->addr, bw, bw_prev); + err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_CHWIDTH, bw); if (err) { ath12k_warn(ar->ab, "failed to update STA %pM peer to bandwidth %d: %d\n", - sta->addr, bw, err); - goto err_rc_bw_changed; + arsta->addr, bw, err); + return; } - err = ath12k_wmi_set_peer_param(ar, sta->addr, + err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_PHYMODE, peer_phymode); if (err) ath12k_warn(ar->ab, "failed to update STA %pM to peer phymode %d: %d\n", - sta->addr, peer_phymode, err); + arsta->addr, peer_phymode, err); } } if (changed & IEEE80211_RC_NSS_CHANGED) { ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac update sta %pM nss %d\n", - sta->addr, nss); + arsta->addr, nss); - err = ath12k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_NSS, nss); if (err) ath12k_warn(ar->ab, "failed to update STA %pM nss %d: %d\n", - sta->addr, nss, err); + arsta->addr, nss, err); } if (changed & IEEE80211_RC_SMPS_CHANGED) { ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac update sta %pM smps %d\n", - sta->addr, smps); + arsta->addr, smps); - err = ath12k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_MIMO_PS_STATE, smps); if (err) ath12k_warn(ar->ab, "failed to update STA %pM smps %d: %d\n", - sta->addr, smps, err); + arsta->addr, smps, err); } if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { @@ -3798,39 +5390,74 @@ static void ath12k_sta_rc_update_wk(struct work_struct *wk) * TODO: Check RATEMASK_CMDID to support auto rates selection * across HT/VHT and for multiple VHT MCS support. */ - if (sta->deflink.vht_cap.vht_supported && num_vht_rates == 1) { - ath12k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + link_sta = ath12k_mac_get_link_sta(arsta); + if (!link_sta) { + ath12k_warn(ar->ab, "unable to access link sta in peer assoc he for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + + if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) { + ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask, band); } else { /* If the peer is non-VHT or no fixed VHT rate * is provided in the new bitrate mask we set the * other rates using peer_assoc command. */ - ath12k_peer_assoc_prepare(ar, arvif->vif, sta, - &peer_arg, true); + ath12k_peer_assoc_prepare(ar, arvif, arsta, + peer_arg, true); - err = ath12k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + err = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (err) ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", - sta->addr, arvif->vdev_id, err); + arsta->addr, arvif->vdev_id, err); if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", - sta->addr, arvif->vdev_id); + arsta->addr, arvif->vdev_id); } } -err_rc_bw_changed: - mutex_unlock(&ar->conf_mutex); } -static int ath12k_mac_inc_num_stations(struct ath12k_vif *arvif, - struct ieee80211_sta *sta) +static void ath12k_mac_free_unassign_link_sta(struct ath12k_hw *ah, + struct ath12k_sta *ahsta, + u8 link_id) +{ + struct ath12k_link_sta *arsta; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS)) + return; + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (WARN_ON(!arsta)) + return; + + ahsta->links_map &= ~BIT(link_id); + rcu_assign_pointer(ahsta->link[link_id], NULL); + synchronize_rcu(); + + if (arsta == &ahsta->deflink) { + arsta->link_id = ATH12K_INVALID_LINK_ID; + arsta->ahsta = NULL; + arsta->arvif = NULL; + return; + } + + kfree(arsta); +} + +static int ath12k_mac_inc_num_stations(struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) { + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct ath12k *ar = arvif->ar; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) + if (arvif->ahvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) return 0; if (ar->num_stations >= ar->max_num_stations) @@ -3841,79 +5468,221 @@ static int ath12k_mac_inc_num_stations(struct ath12k_vif *arvif, return 0; } -static void ath12k_mac_dec_num_stations(struct ath12k_vif *arvif, - struct ieee80211_sta *sta) +static void ath12k_mac_dec_num_stations(struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) { + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct ath12k *ar = arvif->ar; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) + if (arvif->ahvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) return; ar->num_stations--; } +static void ath12k_mac_station_post_remove(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) +{ + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct ath12k_peer *peer; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + ath12k_mac_dec_num_stations(arvif, arsta); + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr); + if (peer && peer->sta == sta) { + ath12k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", + vif->addr, arvif->vdev_id); + peer->sta = NULL; + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + + spin_unlock_bh(&ar->ab->base_lock); + + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; +} + +static int ath12k_mac_station_unauthorize(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) +{ + struct ath12k_peer *peer; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr); + if (peer) + peer->is_authorized = false; + + spin_unlock_bh(&ar->ab->base_lock); + + /* Driver must clear the keys during the state change from + * IEEE80211_STA_AUTHORIZED to IEEE80211_STA_ASSOC, since after + * returning from here, mac80211 is going to delete the keys + * in __sta_info_destroy_part2(). This will ensure that the driver does + * not retain stale key references after mac80211 deletes the keys. + */ + ret = ath12k_clear_peer_keys(arvif, arsta->addr); + if (ret) { + ath12k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +static int ath12k_mac_station_authorize(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) +{ + struct ath12k_peer *peer; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr); + if (peer) + peer->is_authorized = true; + + spin_unlock_bh(&ar->ab->base_lock); + + if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, + arvif->vdev_id, + WMI_PEER_AUTHORIZE, + 1); + if (ret) { + ath12k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", + arsta->addr, arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_mac_station_remove(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) +{ + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct ath12k_vif *ahvif = arvif->ahvif; + int ret = 0; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + wiphy_work_cancel(ar->ah->hw->wiphy, &arsta->update_wk); + + if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) { + ath12k_bss_disassoc(ar, arvif); + ret = ath12k_mac_vdev_stop(arvif); + if (ret) + ath12k_warn(ar->ab, "failed to stop vdev %i: %d\n", + arvif->vdev_id, ret); + } + + if (sta->mlo) + return ret; + + ath12k_dp_peer_cleanup(ar, arvif->vdev_id, arsta->addr); + + ret = ath12k_peer_delete(ar, arvif->vdev_id, arsta->addr); + if (ret) + ath12k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", + arsta->addr, arvif->vdev_id); + else + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", + arsta->addr, arvif->vdev_id); + + ath12k_mac_station_post_remove(ar, arvif, arsta); + + if (sta->valid_links) + ath12k_mac_free_unassign_link_sta(ahvif->ah, + arsta->ahsta, arsta->link_id); + + return ret; +} + static int ath12k_mac_station_add(struct ath12k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta) { struct ath12k_base *ab = ar->ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); - struct ath12k_wmi_peer_create_arg peer_param; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct ath12k_wmi_peer_create_arg peer_param = {0}; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - ret = ath12k_mac_inc_num_stations(arvif, sta); + ret = ath12k_mac_inc_num_stations(arvif, arsta); if (ret) { ath12k_warn(ab, "refusing to associate station: too many connected already (%d)\n", ar->max_num_stations); goto exit; } - arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); - if (!arsta->rx_stats) { - ret = -ENOMEM; - goto dec_num_station; + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar) && !arsta->rx_stats) { + arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); + if (!arsta->rx_stats) { + ret = -ENOMEM; + goto dec_num_station; + } } peer_param.vdev_id = arvif->vdev_id; - peer_param.peer_addr = sta->addr; + peer_param.peer_addr = arsta->addr; peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + peer_param.ml_enabled = sta->mlo; ret = ath12k_peer_create(ar, arvif, sta, &peer_param); if (ret) { ath12k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); + arsta->addr, arvif->vdev_id); goto free_peer; } ath12k_dbg(ab, ATH12K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); + arsta->addr, arvif->vdev_id); if (ieee80211_vif_is_mesh(vif)) { - ret = ath12k_wmi_set_peer_param(ar, sta->addr, + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_USE_4ADDR, 1); if (ret) { ath12k_warn(ab, "failed to STA %pM 4addr capability: %d\n", - sta->addr, ret); + arsta->addr, ret); goto free_peer; } } - ret = ath12k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); + ret = ath12k_dp_peer_setup(ar, arvif->vdev_id, arsta->addr); if (ret) { ath12k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", - sta->addr, arvif->vdev_id, ret); + arsta->addr, arvif->vdev_id, ret); goto free_peer; } if (ab->hw_params->vdev_start_delay && !arvif->is_started && - arvif->vdev_type != WMI_VDEV_TYPE_AP) { + arvif->ahvif->vdev_type != WMI_VDEV_TYPE_AP) { ret = ath12k_start_vdev_delay(ar, arvif); if (ret) { ath12k_warn(ab, "failed to delay vdev start: %d\n", ret); @@ -3921,169 +5690,674 @@ static int ath12k_mac_station_add(struct ath12k *ar, } } + ewma_avg_rssi_init(&arsta->avg_rssi); return 0; free_peer: - ath12k_peer_delete(ar, arvif->vdev_id, sta->addr); + ath12k_peer_delete(ar, arvif->vdev_id, arsta->addr); + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; dec_num_station: - ath12k_mac_dec_num_stations(arvif, sta); + ath12k_mac_dec_num_stations(arvif, arsta); exit: return ret; } -static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, - struct ieee80211_sta *sta) +static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, + struct ath12k_sta *ahsta, + struct ath12k_link_sta *arsta, + struct ath12k_vif *ahvif, + u8 link_id) { - u32 bw = WMI_PEER_CHWIDTH_20MHZ; + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); + struct ieee80211_link_sta *link_sta; + struct ath12k_link_vif *arvif; - switch (sta->deflink.bandwidth) { - case IEEE80211_STA_RX_BW_20: - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - case IEEE80211_STA_RX_BW_40: - bw = WMI_PEER_CHWIDTH_40MHZ; - break; - case IEEE80211_STA_RX_BW_80: - bw = WMI_PEER_CHWIDTH_80MHZ; - break; - case IEEE80211_STA_RX_BW_160: - bw = WMI_PEER_CHWIDTH_160MHZ; - break; - case IEEE80211_STA_RX_BW_320: - bw = WMI_PEER_CHWIDTH_320MHZ; - break; - default: - ath12k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", - sta->deflink.bandwidth, sta->addr); - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - } + lockdep_assert_wiphy(ah->hw->wiphy); - return bw; + if (!arsta || link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return -EINVAL; + + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + if (!arvif) + return -EINVAL; + + memset(arsta, 0, sizeof(*arsta)); + + link_sta = wiphy_dereference(ah->hw->wiphy, sta->link[link_id]); + if (!link_sta) + return -EINVAL; + + ether_addr_copy(arsta->addr, link_sta->addr); + + /* logical index of the link sta in order of creation */ + arsta->link_idx = ahsta->num_peer++; + + arsta->link_id = link_id; + ahsta->links_map |= BIT(arsta->link_id); + arsta->arvif = arvif; + arsta->ahsta = ahsta; + ahsta->ahvif = ahvif; + + wiphy_work_init(&arsta->update_wk, ath12k_sta_rc_update_wk); + + rcu_assign_pointer(ahsta->link[link_id], arsta); + + return 0; } -static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) +static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif, + struct ath12k_sta *ahsta) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); + struct ath12k_hw *ah = ahvif->ah; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + unsigned long links; struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); - struct ath12k_peer *peer; - int ret = 0; + u8 link_id; - /* cancel must be done outside the mutex to avoid deadlock */ - if ((old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST)) - cancel_work_sync(&arsta->update_wk); + lockdep_assert_wiphy(ah->hw->wiphy); - ar = ath12k_ah_to_ar(ah); + ath12k_peer_mlo_link_peers_delete(ahvif, ahsta); - mutex_lock(&ar->conf_mutex); + /* validate link station removal and clear arsta links */ + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arvif || !arsta) + continue; - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) { - memset(arsta, 0, sizeof(*arsta)); - arsta->arvif = arvif; - INIT_WORK(&arsta->update_wk, ath12k_sta_rc_update_wk); + ar = arvif->ar; - ret = ath12k_mac_station_add(ar, vif, sta); - if (ret) - ath12k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - } else if ((old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST)) { - if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { - ath12k_bss_disassoc(ar, arvif); - ret = ath12k_mac_vdev_stop(arvif); - if (ret) - ath12k_warn(ar->ab, "failed to stop vdev %i: %d\n", - arvif->vdev_id, ret); + ath12k_mac_station_post_remove(ar, arvif, arsta); + + ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id); + } + + ath12k_peer_ml_delete(ah, sta); +} + +static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, + struct ath12k_link_vif *arvif, + struct ath12k_link_sta *arsta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_bss_conf *link_conf; + struct ath12k *ar = arvif->ar; + struct ath12k_reg_info *reg_info; + struct ath12k_base *ab = ar->ab; + int ret = 0; + + lockdep_assert_wiphy(hw->wiphy); + + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac handle link %u sta %pM state %d -> %d\n", + arsta->link_id, arsta->addr, old_state, new_state); + + /* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: Remove the station + * from driver + */ + if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + ret = ath12k_mac_station_remove(ar, arvif, arsta); + if (ret) { + ath12k_warn(ab, "Failed to remove station: %pM for VDEV: %d\n", + arsta->addr, arvif->vdev_id); + goto exit; } - ath12k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + } - ret = ath12k_peer_delete(ar, arvif->vdev_id, sta->addr); + /* IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE: Add new station to driver */ + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) - ath12k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - - ath12k_mac_dec_num_stations(arvif, sta); - spin_lock_bh(&ar->ab->base_lock); - peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer && peer->sta == sta) { - ath12k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", - vif->addr, arvif->vdev_id); - peer->sta = NULL; - list_del(&peer->list); - kfree(peer); - ar->num_peers--; - } - spin_unlock_bh(&ar->ab->base_lock); + ath12k_warn(ab, "Failed to add station: %pM for VDEV: %d\n", + arsta->addr, arvif->vdev_id); - kfree(arsta->rx_stats); - arsta->rx_stats = NULL; + /* IEEE80211_STA_AUTH -> IEEE80211_STA_ASSOC: Send station assoc command for + * peer associated to AP/Mesh/ADHOC vif type. + */ } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC && (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { - ret = ath12k_station_assoc(ar, vif, sta, false); + ret = ath12k_mac_station_assoc(ar, arvif, arsta, false); if (ret) - ath12k_warn(ar->ab, "Failed to associate station: %pM\n", - sta->addr); - - spin_lock_bh(&ar->data_lock); + ath12k_warn(ab, "Failed to associate station: %pM\n", + arsta->addr); - arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta); - arsta->bw_prev = sta->deflink.bandwidth; - - spin_unlock_bh(&ar->data_lock); + /* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTHORIZED: set peer status as + * authorized + */ } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { - spin_lock_bh(&ar->ab->base_lock); - - peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer) - peer->is_authorized = true; - - spin_unlock_bh(&ar->ab->base_lock); + ret = ath12k_mac_station_authorize(ar, arvif, arsta); + if (ret) { + ath12k_warn(ab, "Failed to authorize station: %pM\n", + arsta->addr); + goto exit; + } - if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { - ret = ath12k_wmi_set_peer_param(ar, sta->addr, - arvif->vdev_id, - WMI_PEER_AUTHORIZE, - 1); - if (ret) - ath12k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", - sta->addr, arvif->vdev_id, ret); + if (ath12k_wmi_supports_6ghz_cc_ext(ar) && + arvif->ahvif->vdev_type == WMI_VDEV_TYPE_STA) { + link_conf = ath12k_mac_get_link_bss_conf(arvif); + reg_info = ab->reg_info[ar->pdev_idx]; + ath12k_dbg(ab, ATH12K_DBG_MAC, "connection done, update reg rules\n"); + ath12k_hw_to_ah(hw)->regd_updated = false; + ath12k_reg_handle_chan_list(ab, reg_info, arvif->ahvif->vdev_type, + link_conf->power_type); } + + /* IEEE80211_STA_AUTHORIZED -> IEEE80211_STA_ASSOC: station may be in removal, + * deauthorize it. + */ } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { - spin_lock_bh(&ar->ab->base_lock); - - peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer) - peer->is_authorized = false; + ath12k_mac_station_unauthorize(ar, arvif, arsta); - spin_unlock_bh(&ar->ab->base_lock); + /* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTH: disassoc peer connected to + * AP/mesh/ADHOC vif type. + */ } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { - ret = ath12k_station_disassoc(ar, vif, sta); + ret = ath12k_mac_station_disassoc(ar, arvif, arsta); if (ret) - ath12k_warn(ar->ab, "Failed to disassociate station: %pM\n", - sta->addr); + ath12k_warn(ab, "Failed to disassociate station: %pM\n", + arsta->addr); + } + +exit: + return ret; +} + +static bool ath12k_mac_is_freq_on_mac(struct ath12k_hw_mode_freq_range_arg *freq_range, + u32 freq, u8 mac_id) +{ + return (freq >= freq_range[mac_id].low_2ghz_freq && + freq <= freq_range[mac_id].high_2ghz_freq) || + (freq >= freq_range[mac_id].low_5ghz_freq && + freq <= freq_range[mac_id].high_5ghz_freq); +} + +static bool +ath12k_mac_2_freq_same_mac_in_freq_range(struct ath12k_base *ab, + struct ath12k_hw_mode_freq_range_arg *freq_range, + u32 freq_link1, u32 freq_link2) +{ + u8 i; + + for (i = 0; i < MAX_RADIOS; i++) { + if (ath12k_mac_is_freq_on_mac(freq_range, freq_link1, i) && + ath12k_mac_is_freq_on_mac(freq_range, freq_link2, i)) + return true; } - mutex_unlock(&ar->conf_mutex); + return false; +} + +static bool ath12k_mac_is_hw_dbs_capable(struct ath12k_base *ab) +{ + return test_bit(WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT, + ab->wmi_ab.svc_map) && + ab->wmi_ab.hw_mode_info.support_dbs; +} + +static bool ath12k_mac_2_freq_same_mac_in_dbs(struct ath12k_base *ab, + u32 freq_link1, u32 freq_link2) +{ + struct ath12k_hw_mode_freq_range_arg *freq_range; + + if (!ath12k_mac_is_hw_dbs_capable(ab)) + return true; + + freq_range = ab->wmi_ab.hw_mode_info.freq_range_caps[ATH12K_HW_MODE_DBS]; + return ath12k_mac_2_freq_same_mac_in_freq_range(ab, freq_range, + freq_link1, freq_link2); +} + +static bool ath12k_mac_is_hw_sbs_capable(struct ath12k_base *ab) +{ + return test_bit(WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT, + ab->wmi_ab.svc_map) && + ab->wmi_ab.hw_mode_info.support_sbs; +} + +static bool ath12k_mac_2_freq_same_mac_in_sbs(struct ath12k_base *ab, + u32 freq_link1, u32 freq_link2) +{ + struct ath12k_hw_mode_info *info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *sbs_uppr_share; + struct ath12k_hw_mode_freq_range_arg *sbs_low_share; + struct ath12k_hw_mode_freq_range_arg *sbs_range; + + if (!ath12k_mac_is_hw_sbs_capable(ab)) + return true; + + if (ab->wmi_ab.sbs_lower_band_end_freq) { + sbs_uppr_share = info->freq_range_caps[ATH12K_HW_MODE_SBS_UPPER_SHARE]; + sbs_low_share = info->freq_range_caps[ATH12K_HW_MODE_SBS_LOWER_SHARE]; + + return ath12k_mac_2_freq_same_mac_in_freq_range(ab, sbs_low_share, + freq_link1, freq_link2) || + ath12k_mac_2_freq_same_mac_in_freq_range(ab, sbs_uppr_share, + freq_link1, freq_link2); + } + + sbs_range = info->freq_range_caps[ATH12K_HW_MODE_SBS]; + return ath12k_mac_2_freq_same_mac_in_freq_range(ab, sbs_range, + freq_link1, freq_link2); +} + +static bool ath12k_mac_freqs_on_same_mac(struct ath12k_base *ab, + u32 freq_link1, u32 freq_link2) +{ + return ath12k_mac_2_freq_same_mac_in_dbs(ab, freq_link1, freq_link2) && + ath12k_mac_2_freq_same_mac_in_sbs(ab, freq_link1, freq_link2); +} + +static int ath12k_mac_mlo_sta_set_link_active(struct ath12k_base *ab, + enum wmi_mlo_link_force_reason reason, + enum wmi_mlo_link_force_mode mode, + u8 *mlo_vdev_id_lst, + u8 num_mlo_vdev, + u8 *mlo_inactive_vdev_lst, + u8 num_mlo_inactive_vdev) +{ + struct wmi_mlo_link_set_active_arg param = {0}; + u32 entry_idx, entry_offset, vdev_idx; + u8 vdev_id; + + param.reason = reason; + param.force_mode = mode; + + for (vdev_idx = 0; vdev_idx < num_mlo_vdev; vdev_idx++) { + vdev_id = mlo_vdev_id_lst[vdev_idx]; + entry_idx = vdev_id / 32; + entry_offset = vdev_id % 32; + if (entry_idx >= WMI_MLO_LINK_NUM_SZ) { + ath12k_warn(ab, "Invalid entry_idx %d num_mlo_vdev %d vdev %d", + entry_idx, num_mlo_vdev, vdev_id); + return -EINVAL; + } + param.vdev_bitmap[entry_idx] |= 1 << entry_offset; + /* update entry number if entry index changed */ + if (param.num_vdev_bitmap < entry_idx + 1) + param.num_vdev_bitmap = entry_idx + 1; + } + + ath12k_dbg(ab, ATH12K_DBG_MAC, + "num_vdev_bitmap %d vdev_bitmap[0] = 0x%x, vdev_bitmap[1] = 0x%x", + param.num_vdev_bitmap, param.vdev_bitmap[0], param.vdev_bitmap[1]); + + if (mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE) { + for (vdev_idx = 0; vdev_idx < num_mlo_inactive_vdev; vdev_idx++) { + vdev_id = mlo_inactive_vdev_lst[vdev_idx]; + entry_idx = vdev_id / 32; + entry_offset = vdev_id % 32; + if (entry_idx >= WMI_MLO_LINK_NUM_SZ) { + ath12k_warn(ab, "Invalid entry_idx %d num_mlo_vdev %d vdev %d", + entry_idx, num_mlo_inactive_vdev, vdev_id); + return -EINVAL; + } + param.inactive_vdev_bitmap[entry_idx] |= 1 << entry_offset; + /* update entry number if entry index changed */ + if (param.num_inactive_vdev_bitmap < entry_idx + 1) + param.num_inactive_vdev_bitmap = entry_idx + 1; + } + + ath12k_dbg(ab, ATH12K_DBG_MAC, + "num_vdev_bitmap %d inactive_vdev_bitmap[0] = 0x%x, inactive_vdev_bitmap[1] = 0x%x", + param.num_inactive_vdev_bitmap, + param.inactive_vdev_bitmap[0], + param.inactive_vdev_bitmap[1]); + } + + if (mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_LINK_NUM || + mode == WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM) { + param.num_link_entry = 1; + param.link_num[0].num_of_link = num_mlo_vdev - 1; + } + + return ath12k_wmi_send_mlo_link_set_active_cmd(ab, ¶m); +} + +static int ath12k_mac_mlo_sta_update_link_active(struct ath12k_base *ab, + struct ieee80211_hw *hw, + struct ath12k_vif *ahvif) +{ + u8 mlo_vdev_id_lst[IEEE80211_MLD_MAX_NUM_LINKS] = {0}; + u32 mlo_freq_list[IEEE80211_MLD_MAX_NUM_LINKS] = {0}; + unsigned long links = ahvif->links_map; + enum wmi_mlo_link_force_reason reason; + struct ieee80211_chanctx_conf *conf; + enum wmi_mlo_link_force_mode mode; + struct ieee80211_bss_conf *info; + struct ath12k_link_vif *arvif; + u8 num_mlo_vdev = 0; + u8 link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + /* make sure vdev is created on this device */ + if (!arvif || !arvif->is_created || arvif->ar->ab != ab) + continue; + + info = ath12k_mac_get_link_bss_conf(arvif); + conf = wiphy_dereference(hw->wiphy, info->chanctx_conf); + mlo_freq_list[num_mlo_vdev] = conf->def.chan->center_freq; + + mlo_vdev_id_lst[num_mlo_vdev] = arvif->vdev_id; + num_mlo_vdev++; + } + + /* It is not allowed to activate more links than a single device + * supported. Something goes wrong if we reach here. + */ + if (num_mlo_vdev > ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE) { + WARN_ON_ONCE(1); + return -EINVAL; + } + + /* if 2 links are established and both link channels fall on the + * same hardware MAC, send command to firmware to deactivate one + * of them. + */ + if (num_mlo_vdev == 2 && + ath12k_mac_freqs_on_same_mac(ab, mlo_freq_list[0], + mlo_freq_list[1])) { + mode = WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM; + reason = WMI_MLO_LINK_FORCE_REASON_NEW_CONNECT; + return ath12k_mac_mlo_sta_set_link_active(ab, reason, mode, + mlo_vdev_id_lst, num_mlo_vdev, + NULL, 0); + } + + return 0; +} + +static bool ath12k_mac_are_sbs_chan(struct ath12k_base *ab, u32 freq_1, u32 freq_2) +{ + if (!ath12k_mac_is_hw_sbs_capable(ab)) + return false; + + if (ath12k_is_2ghz_channel_freq(freq_1) || + ath12k_is_2ghz_channel_freq(freq_2)) + return false; + + return !ath12k_mac_2_freq_same_mac_in_sbs(ab, freq_1, freq_2); +} + +static bool ath12k_mac_are_dbs_chan(struct ath12k_base *ab, u32 freq_1, u32 freq_2) +{ + if (!ath12k_mac_is_hw_dbs_capable(ab)) + return false; + + return !ath12k_mac_2_freq_same_mac_in_dbs(ab, freq_1, freq_2); +} + +static int ath12k_mac_select_links(struct ath12k_base *ab, + struct ieee80211_vif *vif, + struct ieee80211_hw *hw, + u16 *selected_links) +{ + unsigned long useful_links = ieee80211_vif_usable_links(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + u8 num_useful_links = hweight_long(useful_links); + struct ieee80211_chanctx_conf *chanctx; + struct ath12k_link_vif *assoc_arvif; + u32 assoc_link_freq, partner_freq; + u16 sbs_links = 0, dbs_links = 0; + struct ieee80211_bss_conf *info; + struct ieee80211_channel *chan; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + u8 link_id; + + /* activate all useful links if less than max supported */ + if (num_useful_links <= ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE) { + *selected_links = useful_links; + return 0; + } + + /* only in station mode we can get here, so it's safe + * to use ap_addr + */ + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); + if (!sta) { + rcu_read_unlock(); + ath12k_warn(ab, "failed to find sta with addr %pM\n", vif->cfg.ap_addr); + return -EINVAL; + } + + ahsta = ath12k_sta_to_ahsta(sta); + assoc_arvif = wiphy_dereference(hw->wiphy, ahvif->link[ahsta->assoc_link_id]); + info = ath12k_mac_get_link_bss_conf(assoc_arvif); + chanctx = rcu_dereference(info->chanctx_conf); + assoc_link_freq = chanctx->def.chan->center_freq; + rcu_read_unlock(); + ath12k_dbg(ab, ATH12K_DBG_MAC, "assoc link %u freq %u\n", + assoc_arvif->link_id, assoc_link_freq); + + /* assoc link is already activated and has to be kept active, + * only need to select a partner link from others. + */ + useful_links &= ~BIT(assoc_arvif->link_id); + for_each_set_bit(link_id, &useful_links, IEEE80211_MLD_MAX_NUM_LINKS) { + info = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); + if (!info) { + ath12k_warn(ab, "failed to get link info for link: %u\n", + link_id); + return -ENOLINK; + } + + chan = info->chanreq.oper.chan; + if (!chan) { + ath12k_warn(ab, "failed to get chan for link: %u\n", link_id); + return -EINVAL; + } + + partner_freq = chan->center_freq; + if (ath12k_mac_are_sbs_chan(ab, assoc_link_freq, partner_freq)) { + sbs_links |= BIT(link_id); + ath12k_dbg(ab, ATH12K_DBG_MAC, "new SBS link %u freq %u\n", + link_id, partner_freq); + continue; + } + + if (ath12k_mac_are_dbs_chan(ab, assoc_link_freq, partner_freq)) { + dbs_links |= BIT(link_id); + ath12k_dbg(ab, ATH12K_DBG_MAC, "new DBS link %u freq %u\n", + link_id, partner_freq); + continue; + } + + ath12k_dbg(ab, ATH12K_DBG_MAC, "non DBS/SBS link %u freq %u\n", + link_id, partner_freq); + } + + /* choose the first candidate no matter how many is in the list */ + if (sbs_links) + link_id = __ffs(sbs_links); + else if (dbs_links) + link_id = __ffs(dbs_links); + else + link_id = ffs(useful_links) - 1; + + ath12k_dbg(ab, ATH12K_DBG_MAC, "select partner link %u\n", link_id); + + *selected_links = BIT(assoc_arvif->link_id) | BIT(link_id); + + return 0; +} + +static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_base *prev_ab = NULL, *ab; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + unsigned long valid_links; + u16 selected_links = 0; + u8 link_id = 0, i; + struct ath12k *ar; + int ret; + + lockdep_assert_wiphy(hw->wiphy); + + if (ieee80211_vif_is_mld(vif) && sta->valid_links) { + WARN_ON(!sta->mlo && hweight16(sta->valid_links) != 1); + link_id = ffs(sta->valid_links) - 1; + } + + /* IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE: + * New station add received. If this is a ML station then + * ahsta->links_map will be zero and sta->valid_links will be 1. + * Assign default link to the first link sta. + */ + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + memset(ahsta, 0, sizeof(*ahsta)); + + arsta = &ahsta->deflink; + + /* ML sta */ + if (sta->mlo && !ahsta->links_map && + (hweight16(sta->valid_links) == 1)) { + ret = ath12k_peer_ml_create(ah, sta); + if (ret) { + ath12k_hw_warn(ah, "unable to create ML peer for sta %pM", + sta->addr); + goto exit; + } + } + + ret = ath12k_mac_assign_link_sta(ah, ahsta, arsta, ahvif, + link_id); + if (ret) { + ath12k_hw_warn(ah, "unable assign link %d for sta %pM", + link_id, sta->addr); + goto exit; + } + + /* above arsta will get memset, hence do this after assign + * link sta + */ + if (sta->mlo) { + /* For station mode, arvif->is_sta_assoc_link has been set when + * vdev starts. Make sure the arvif/arsta pair have same setting + */ + if (vif->type == NL80211_IFTYPE_STATION && + !arsta->arvif->is_sta_assoc_link) { + ath12k_hw_warn(ah, "failed to verify assoc link setting with link id %u\n", + link_id); + ret = -EINVAL; + goto exit; + } + + arsta->is_assoc_link = true; + ahsta->assoc_link_id = link_id; + } + } + + /* In the ML station scenario, activate all partner links once the + * client is transitioning to the associated state. + * + * FIXME: Ideally, this activation should occur when the client + * transitions to the authorized state. However, there are some + * issues with handling this in the firmware. Until the firmware + * can manage it properly, activate the links when the client is + * about to move to the associated state. + */ + if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION && + old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { + /* TODO: for now only do link selection for single device + * MLO case. Other cases would be handled in the future. + */ + ab = ah->radio[0].ab; + if (ab->ag->num_devices == 1) { + ret = ath12k_mac_select_links(ab, vif, hw, &selected_links); + if (ret) { + ath12k_warn(ab, + "failed to get selected links: %d\n", ret); + goto exit; + } + } else { + selected_links = ieee80211_vif_usable_links(vif); + } + + ieee80211_set_active_links(vif, selected_links); + } + + /* Handle all the other state transitions in generic way */ + valid_links = ahsta->links_map; + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + arsta = wiphy_dereference(hw->wiphy, ahsta->link[link_id]); + /* some assumptions went wrong! */ + if (WARN_ON(!arvif || !arsta)) + continue; + + /* vdev might be in deleted */ + if (WARN_ON(!arvif->ar)) + continue; + + ret = ath12k_mac_handle_link_sta_state(hw, arvif, arsta, + old_state, new_state); + if (ret) { + ath12k_hw_warn(ah, "unable to move link sta %d of sta %pM from state %d to %d", + link_id, arsta->addr, old_state, new_state); + goto exit; + } + } + + if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION && + old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { + for_each_ar(ah, ar, i) { + ab = ar->ab; + if (prev_ab == ab) + continue; + + ret = ath12k_mac_mlo_sta_update_link_active(ab, hw, ahvif); + if (ret) { + ath12k_warn(ab, + "failed to update link active state on connect %d\n", + ret); + goto exit; + } + + prev_ab = ab; + } + } + /* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: + * Remove the station from driver (handle ML sta here since that + * needs special handling. Normal sta will be handled in generic + * handler below + */ + if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST && sta->mlo) + ath12k_mac_ml_station_remove(ahvif, ahsta); + + ret = 0; + +exit: + /* update the state if everything went well */ + if (!ret) + ahsta->state = new_state; return ret; } @@ -4092,28 +6366,41 @@ static int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + u8 link_id; int ret; s16 txpwr; + lockdep_assert_wiphy(hw->wiphy); + + /* TODO: use link id from mac80211 once that's implemented */ + link_id = 0; + + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + arsta = wiphy_dereference(hw->wiphy, ahsta->link[link_id]); + if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) { txpwr = 0; } else { txpwr = sta->deflink.txpwr.power; - if (!txpwr) - return -EINVAL; + if (!txpwr) { + ret = -EINVAL; + goto out; + } } - if (txpwr > ATH12K_TX_POWER_MAX_VAL || txpwr < ATH12K_TX_POWER_MIN_VAL) - return -EINVAL; - - ar = ath12k_ah_to_ar(ah); + if (txpwr > ATH12K_TX_POWER_MAX_VAL || txpwr < ATH12K_TX_POWER_MIN_VAL) { + ret = -EINVAL; + goto out; + } - mutex_lock(&ar->conf_mutex); + ar = arvif->ar; - ret = ath12k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_USE_FIXED_PWR, txpwr); if (ret) { ath12k_warn(ar->ab, "failed to set tx power for station ret: %d\n", @@ -4122,56 +6409,88 @@ static int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, } out: - mutex_unlock(&ar->conf_mutex); return ret; } -static void ath12k_mac_op_sta_rc_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u32 changed) +static void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ieee80211_sta *sta = link_sta->sta; struct ath12k *ar; - struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_link_sta *arsta; + struct ath12k_link_vif *arvif; struct ath12k_peer *peer; u32 bw, smps; - ar = ath12k_ah_to_ar(ah); + rcu_read_lock(); + arvif = rcu_dereference(ahvif->link[link_sta->link_id]); + if (!arvif) { + ath12k_hw_warn(ah, "mac sta rc update failed to fetch link vif on link id %u for peer %pM\n", + link_sta->link_id, sta->addr); + rcu_read_unlock(); + return; + } + + ar = arvif->ar; + arsta = rcu_dereference(ahsta->link[link_sta->link_id]); + if (!arsta) { + rcu_read_unlock(); + ath12k_warn(ar->ab, "mac sta rc update failed to fetch link sta on link id %u for peer %pM\n", + link_sta->link_id, sta->addr); + return; + } spin_lock_bh(&ar->ab->base_lock); - peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr); if (!peer) { spin_unlock_bh(&ar->ab->base_lock); + rcu_read_unlock(); ath12k_warn(ar->ab, "mac sta rc update failed to find peer %pM on vdev %i\n", - sta->addr, arvif->vdev_id); + arsta->addr, arvif->vdev_id); return; } spin_unlock_bh(&ar->ab->base_lock); + if (arsta->link_id >= IEEE80211_MLD_MAX_NUM_LINKS) { + rcu_read_unlock(); + return; + } + + link_sta = rcu_dereference(sta->link[arsta->link_id]); + if (!link_sta) { + rcu_read_unlock(); + ath12k_warn(ar->ab, "unable to access link sta in rc update for sta %pM link %u\n", + sta->addr, arsta->link_id); + return; + } + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", - sta->addr, changed, sta->deflink.bandwidth, sta->deflink.rx_nss, - sta->deflink.smps_mode); + arsta->addr, changed, link_sta->bandwidth, link_sta->rx_nss, + link_sta->smps_mode); spin_lock_bh(&ar->data_lock); if (changed & IEEE80211_RC_BW_CHANGED) { - bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta); arsta->bw_prev = arsta->bw; arsta->bw = bw; } if (changed & IEEE80211_RC_NSS_CHANGED) - arsta->nss = sta->deflink.rx_nss; + arsta->nss = link_sta->rx_nss; if (changed & IEEE80211_RC_SMPS_CHANGED) { smps = WMI_PEER_SMPS_PS_NONE; - switch (sta->deflink.smps_mode) { + switch (link_sta->smps_mode) { case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_OFF: smps = WMI_PEER_SMPS_PS_NONE; @@ -4183,8 +6502,8 @@ static void ath12k_mac_op_sta_rc_update(struct ieee80211_hw *hw, smps = WMI_PEER_SMPS_DYNAMIC; break; default: - ath12k_warn(ar->ab, "Invalid smps %d in sta rc update for %pM\n", - sta->deflink.smps_mode, sta->addr); + ath12k_warn(ar->ab, "Invalid smps %d in sta rc update for %pM link %u\n", + link_sta->smps_mode, arsta->addr, link_sta->link_id); smps = WMI_PEER_SMPS_PS_NONE; break; } @@ -4196,17 +6515,124 @@ static void ath12k_mac_op_sta_rc_update(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); - ieee80211_queue_work(hw, &arsta->update_wk); + wiphy_work_queue(hw->wiphy, &arsta->update_wk); + + rcu_read_unlock(); +} + +static struct ath12k_link_sta *ath12k_mac_alloc_assign_link_sta(struct ath12k_hw *ah, + struct ath12k_sta *ahsta, + struct ath12k_vif *ahvif, + u8 link_id) +{ + struct ath12k_link_sta *arsta; + int ret; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (arsta) + return NULL; + + arsta = kmalloc(sizeof(*arsta), GFP_KERNEL); + if (!arsta) + return NULL; + + ret = ath12k_mac_assign_link_sta(ah, ahsta, arsta, ahvif, link_id); + if (ret) { + kfree(arsta); + return NULL; + } + + return arsta; } -static int ath12k_conf_tx_uapsd(struct ath12k_vif *arvif, +static int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_hw *ah = hw->priv; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + unsigned long valid_links; + struct ath12k *ar; + u8 link_id; + int ret; + + lockdep_assert_wiphy(hw->wiphy); + + if (!sta->valid_links) + return -EINVAL; + + /* Firmware does not support removal of one of link stas. All sta + * would be removed during ML STA delete in sta_state(), hence link + * sta removal is not handled here. + */ + if (new_links < old_links) + return 0; + + if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) { + ath12k_hw_warn(ah, "unable to add link for ml sta %pM", sta->addr); + return -EINVAL; + } + + /* this op is expected only after initial sta insertion with default link */ + if (WARN_ON(ahsta->links_map == 0)) + return -EINVAL; + + valid_links = new_links; + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + if (ahsta->links_map & BIT(link_id)) + continue; + + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + + if (!arvif || !arsta) { + ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); + continue; + } + + ar = arvif->ar; + if (!ar) + continue; + + ret = ath12k_mac_station_add(ar, arvif, arsta); + if (ret) { + ath12k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", + arsta->addr, arvif->vdev_id); + ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id); + return ret; + } + } + + return 0; +} + +static bool ath12k_mac_op_can_activate_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 active_links) +{ + /* TODO: Handle recovery case */ + + return true; +} + +static int ath12k_conf_tx_uapsd(struct ath12k_link_vif *arvif, u16 ac, bool enable) { struct ath12k *ar = arvif->ar; + struct ath12k_vif *ahvif = arvif->ahvif; u32 value; int ret; - if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + if (ahvif->vdev_type != WMI_VDEV_TYPE_STA) return 0; switch (ac) { @@ -4229,19 +6655,19 @@ static int ath12k_conf_tx_uapsd(struct ath12k_vif *arvif, } if (enable) - arvif->u.sta.uapsd |= value; + ahvif->u.sta.uapsd |= value; else - arvif->u.sta.uapsd &= ~value; + ahvif->u.sta.uapsd &= ~value; ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id, WMI_STA_PS_PARAM_UAPSD, - arvif->u.sta.uapsd); + ahvif->u.sta.uapsd); if (ret) { ath12k_warn(ar->ab, "could not set uapsd params %d\n", ret); goto exit; } - if (arvif->u.sta.uapsd) + if (ahvif->u.sta.uapsd) value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; else value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; @@ -4256,8 +6682,7 @@ exit: return ret; } -static int ath12k_mac_conf_tx(struct ath12k_vif *arvif, - unsigned int link_id, u16 ac, +static int ath12k_mac_conf_tx(struct ath12k_link_vif *arvif, u16 ac, const struct ieee80211_tx_queue_params *params) { struct wmi_wmm_params_arg *p = NULL; @@ -4265,7 +6690,7 @@ static int ath12k_mac_conf_tx(struct ath12k_vif *arvif, struct ath12k_base *ab = ar->ab; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); switch (ac) { case IEEE80211_AC_VO: @@ -4314,16 +6739,30 @@ static int ath12k_mac_op_conf_tx(struct ieee80211_hw *hw, unsigned int link_id, u16 ac, const struct ieee80211_tx_queue_params *params) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + struct ath12k_vif_cache *cache; int ret; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); + + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return -EINVAL; + + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (!arvif || !arvif->is_created) { + cache = ath12k_ahvif_get_link_cache(ahvif, link_id); + if (!cache) + return -ENOSPC; - mutex_lock(&ar->conf_mutex); - ret = ath12k_mac_conf_tx(arvif, link_id, ac, params); - mutex_unlock(&ar->conf_mutex); + cache->tx_conf.changed = true; + cache->tx_conf.ac = ac; + cache->tx_conf.tx_queue_params = *params; + + return 0; + } + + ret = ath12k_mac_conf_tx(arvif, ac, params); return ret; } @@ -4394,10 +6833,11 @@ ath12k_create_ht_cap(struct ath12k *ar, u32 ar_ht_cap, u32 rate_cap_rx_chainmask return ht_cap; } -static int ath12k_mac_set_txbf_conf(struct ath12k_vif *arvif) +static int ath12k_mac_set_txbf_conf(struct ath12k_link_vif *arvif) { u32 value = 0; struct ath12k *ar = arvif->ar; + struct ath12k_vif *ahvif = arvif->ahvif; int nsts; int sound_dim; u32 vht_cap = ar->pdev->cap.vht_cap; @@ -4425,7 +6865,7 @@ static int ath12k_mac_set_txbf_conf(struct ath12k_vif *arvif) value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER; if ((vht_cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) && - arvif->vdev_type == WMI_VDEV_TYPE_AP) + ahvif->vdev_type == WMI_VDEV_TYPE_AP) value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER; } @@ -4433,7 +6873,7 @@ static int ath12k_mac_set_txbf_conf(struct ath12k_vif *arvif) value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE; if ((vht_cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) && - arvif->vdev_type == WMI_VDEV_TYPE_STA) + ahvif->vdev_type == WMI_VDEV_TYPE_STA) value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE; } @@ -4536,7 +6976,7 @@ static void ath12k_mac_setup_ht_vht_cap(struct ath12k *ar, rate_cap_tx_chainmask = ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift; rate_cap_rx_chainmask = ar->cfg_rx_chainmask >> cap->rx_chain_mask_shift; - if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { band = &ar->mac.sbands[NL80211_BAND_2GHZ]; ht_cap = cap->band[NL80211_BAND_2GHZ].ht_cap_info; if (ht_cap_info) @@ -4545,7 +6985,7 @@ static void ath12k_mac_setup_ht_vht_cap(struct ath12k *ar, rate_cap_rx_chainmask); } - if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && + if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP && (ar->ab->hw_params->single_pdev_only || !ar->supports_6ghz)) { band = &ar->mac.sbands[NL80211_BAND_5GHZ]; @@ -4722,6 +7162,8 @@ static void ath12k_mac_copy_he_cap(struct ath12k_band_cap *band_cap, switch (iftype) { case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[2] &= + ~IEEE80211_HE_MAC_CAP2_BCAST_TWT; he_cap_elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK; he_cap_elem->phy_cap_info[9] |= @@ -4867,7 +7309,8 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar, memset(eht_cap, 0, sizeof(struct ieee80211_sta_eht_cap)); - if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map))) + if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map)) || + ath12k_acpi_get_disable_11be(ar->ab)) return; eht_cap->has_eht = true; @@ -4953,7 +7396,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, enum nl80211_band band; int count; - if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { band = NL80211_BAND_2GHZ; count = ath12k_mac_copy_sband_iftype_data(ar, cap, ar->mac.iftype[band], @@ -4963,7 +7406,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, count); } - if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) { + if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { band = NL80211_BAND_5GHZ; count = ath12k_mac_copy_sband_iftype_data(ar, cap, ar->mac.iftype[band], @@ -4973,7 +7416,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, count); } - if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && + if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { band = NL80211_BAND_6GHZ; count = ath12k_mac_copy_sband_iftype_data(ar, cap, @@ -4987,9 +7430,10 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, static int __ath12k_set_antenna(struct ath12k *ar, u32 tx_ant, u32 rx_ant) { + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (ath12k_check_chain_mask(ar, tx_ant, true)) return -EINVAL; @@ -4997,11 +7441,18 @@ static int __ath12k_set_antenna(struct ath12k *ar, u32 tx_ant, u32 rx_ant) if (ath12k_check_chain_mask(ar, rx_ant, false)) return -EINVAL; + /* Since we advertised the max cap of all radios combined during wiphy + * registration, ensure we don't set the antenna config higher than the + * limits + */ + tx_ant = min_t(u32, tx_ant, ar->pdev->cap.tx_chain_mask); + rx_ant = min_t(u32, rx_ant, ar->pdev->cap.rx_chain_mask); + ar->cfg_tx_chainmask = tx_ant; ar->cfg_rx_chainmask = rx_ant; - if (ar->state != ATH12K_STATE_ON && - ar->state != ATH12K_STATE_RESTARTED) + if (ah->state != ATH12K_HW_STATE_ON && + ah->state != ATH12K_HW_STATE_RESTARTED) return 0; ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_TX_CHAIN_MASK, @@ -5035,6 +7486,8 @@ static void ath12k_mgmt_over_wmi_tx_drop(struct ath12k *ar, struct sk_buff *skb) { int num_mgmt; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + ieee80211_free_txskb(ath12k_ar_to_hw(ar), skb); num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); @@ -5086,17 +7539,22 @@ static int ath12k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) return 0; } -static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_vif *arvif, +static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arvif, struct sk_buff *skb) { struct ath12k_base *ab = ar->ab; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); struct ieee80211_tx_info *info; + enum hal_encrypt_type enctype; + unsigned int mic_len; dma_addr_t paddr; int buf_id; int ret; - ATH12K_SKB_CB(skb)->ar = ar; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + skb_cb->ar = ar; spin_lock_bh(&ar->txmgmt_idr_lock); buf_id = idr_alloc(&ar->txmgmt_idr, skb, 0, ATH12K_TX_MGMT_NUM_PENDING_MAX, GFP_ATOMIC); @@ -5105,12 +7563,15 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_vif *arvif, return -ENOSPC; info = IEEE80211_SKB_CB(skb); - if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { + if ((skb_cb->flags & ATH12K_SKB_CIPHER_SET) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { if ((ieee80211_is_action(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) || ieee80211_is_disassoc(hdr->frame_control)) && ieee80211_has_protected(hdr->frame_control)) { - skb_put(skb, IEEE80211_CCMP_MIC_LEN); + enctype = ath12k_dp_tx_get_encrypt_type(skb_cb->cipher); + mic_len = ath12k_dp_rx_crypto_mic_len(ar, enctype); + skb_put(skb, mic_len); } } @@ -5121,7 +7582,7 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_vif *arvif, goto err_free_idr; } - ATH12K_SKB_CB(skb)->paddr = paddr; + skb_cb->paddr = paddr; ret = ath12k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb); if (ret) { @@ -5132,7 +7593,7 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_vif *arvif, return 0; err_unmap_buf: - dma_unmap_single(ab->dev, ATH12K_SKB_CB(skb)->paddr, + dma_unmap_single(ab->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); err_free_idr: spin_lock_bh(&ar->txmgmt_idr_lock); @@ -5150,14 +7611,18 @@ static void ath12k_mgmt_over_wmi_tx_purge(struct ath12k *ar) ath12k_mgmt_over_wmi_tx_drop(ar, skb); } -static void ath12k_mgmt_over_wmi_tx_work(struct work_struct *work) +static void ath12k_mgmt_over_wmi_tx_work(struct wiphy *wiphy, struct wiphy_work *work) { struct ath12k *ar = container_of(work, struct ath12k, wmi_mgmt_tx_work); + struct ath12k_hw *ah = ar->ah; struct ath12k_skb_cb *skb_cb; - struct ath12k_vif *arvif; + struct ath12k_vif *ahvif; + struct ath12k_link_vif *arvif; struct sk_buff *skb; int ret; + lockdep_assert_wiphy(wiphy); + while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) { skb_cb = ATH12K_SKB_CB(skb); if (!skb_cb->vif) { @@ -5166,8 +7631,16 @@ static void ath12k_mgmt_over_wmi_tx_work(struct work_struct *work) continue; } - arvif = ath12k_vif_to_arvif(skb_cb->vif); + ahvif = ath12k_vif_to_ahvif(skb_cb->vif); + if (!(ahvif->links_map & BIT(skb_cb->link_id))) { + ath12k_warn(ar->ab, + "invalid linkid %u in mgmt over wmi tx with linkmap 0x%x\n", + skb_cb->link_id, ahvif->links_map); + ath12k_mgmt_over_wmi_tx_drop(ar, skb); + continue; + } + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[skb_cb->link_id]); if (ar->allocated_vdev_map & (1LL << arvif->vdev_id)) { ret = ath12k_mac_mgmt_tx_wmi(ar, arvif, skb); if (ret) { @@ -5177,8 +7650,9 @@ static void ath12k_mgmt_over_wmi_tx_work(struct work_struct *work) } } else { ath12k_warn(ar->ab, - "dropping mgmt frame for vdev %d, is_started %d\n", + "dropping mgmt frame for vdev %d link %u is_started %d\n", arvif->vdev_id, + skb_cb->link_id, arvif->is_started); ath12k_mgmt_over_wmi_tx_drop(ar, skb); } @@ -5212,7 +7686,7 @@ static int ath12k_mac_mgmt_tx(struct ath12k *ar, struct sk_buff *skb, skb_queue_tail(q, skb); atomic_inc(&ar->num_pending_mgmt_tx); - ieee80211_queue_work(ath12k_ar_to_hw(ar), &ar->wmi_mgmt_tx_work); + wiphy_work_queue(ath12k_ar_to_hw(ar)->wiphy, &ar->wmi_mgmt_tx_work); return 0; } @@ -5222,22 +7696,137 @@ static void ath12k_mac_add_p2p_noa_ie(struct ath12k *ar, struct sk_buff *skb, bool is_prb_rsp) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); if (likely(!is_prb_rsp)) return; spin_lock_bh(&ar->data_lock); - if (arvif->u.ap.noa_data && - !pskb_expand_head(skb, 0, arvif->u.ap.noa_len, + if (ahvif->u.ap.noa_data && + !pskb_expand_head(skb, 0, ahvif->u.ap.noa_len, GFP_ATOMIC)) - skb_put_data(skb, arvif->u.ap.noa_data, - arvif->u.ap.noa_len); + skb_put_data(skb, ahvif->u.ap.noa_data, + ahvif->u.ap.noa_len); spin_unlock_bh(&ar->data_lock); } +/* Note: called under rcu_read_lock() */ +static void ath12k_mlo_mcast_update_tx_link_address(struct ieee80211_vif *vif, + u8 link_id, struct sk_buff *skb, + u32 info_flags) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_bss_conf *bss_conf; + + if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return; + + bss_conf = rcu_dereference(vif->link_conf[link_id]); + if (bss_conf) + ether_addr_copy(hdr->addr2, bss_conf->addr); +} + +/* Note: called under rcu_read_lock() */ +static u8 ath12k_mac_get_tx_link(struct ieee80211_sta *sta, struct ieee80211_vif *vif, + u8 link, struct sk_buff *skb, u32 info_flags) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *bss_conf; + struct ath12k_sta *ahsta; + + /* Use the link id passed or the default vif link */ + if (!sta) { + if (link != IEEE80211_LINK_UNSPECIFIED) + return link; + + return ahvif->deflink.link_id; + } + + ahsta = ath12k_sta_to_ahsta(sta); + + /* Below translation ensures we pass proper A2 & A3 for non ML clients. + * Also it assumes for now support only for MLO AP in this path + */ + if (!sta->mlo) { + link = ahsta->deflink.link_id; + + if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return link; + + bss_conf = rcu_dereference(vif->link_conf[link]); + if (bss_conf) { + ether_addr_copy(hdr->addr2, bss_conf->addr); + if (!ieee80211_has_tods(hdr->frame_control) && + !ieee80211_has_fromds(hdr->frame_control)) + ether_addr_copy(hdr->addr3, bss_conf->addr); + } + + return link; + } + + /* enqueue eth enacap & data frames on primary link, FW does link + * selection and address translation. + */ + if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP || + ieee80211_is_data(hdr->frame_control)) + return ahsta->assoc_link_id; + + /* 802.11 frame cases */ + if (link == IEEE80211_LINK_UNSPECIFIED) + link = ahsta->deflink.link_id; + + if (!ieee80211_is_mgmt(hdr->frame_control)) + return link; + + /* Perform address conversion for ML STA Tx */ + bss_conf = rcu_dereference(vif->link_conf[link]); + link_sta = rcu_dereference(sta->link[link]); + + if (bss_conf && link_sta) { + ether_addr_copy(hdr->addr1, link_sta->addr); + ether_addr_copy(hdr->addr2, bss_conf->addr); + + if (vif->type == NL80211_IFTYPE_STATION && bss_conf->bssid) + ether_addr_copy(hdr->addr3, bss_conf->bssid); + else if (vif->type == NL80211_IFTYPE_AP) + ether_addr_copy(hdr->addr3, bss_conf->addr); + + return link; + } + + if (bss_conf) { + /* In certain cases where a ML sta associated and added subset of + * links on which the ML AP is active, but now sends some frame + * (ex. Probe request) on a different link which is active in our + * MLD but was not added during previous association, we can + * still honor the Tx to that ML STA via the requested link. + * The control would reach here in such case only when that link + * address is same as the MLD address or in worst case clients + * used MLD address at TA wrongly which would have helped + * identify the ML sta object and pass it here. + * If the link address of that STA is different from MLD address, + * then the sta object would be NULL and control won't reach + * here but return at the start of the function itself with !sta + * check. Also this would not need any translation at hdr->addr1 + * from MLD to link address since the RA is the MLD address + * (same as that link address ideally) already. + */ + ether_addr_copy(hdr->addr2, bss_conf->addr); + + if (vif->type == NL80211_IFTYPE_STATION && bss_conf->bssid) + ether_addr_copy(hdr->addr3, bss_conf->bssid); + else if (vif->type == NL80211_IFTYPE_AP) + ether_addr_copy(hdr->addr3, bss_conf->addr); + } + + return link; +} + +/* Note: called under rcu_read_lock() */ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) @@ -5245,14 +7834,31 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k *ar = arvif->ar; + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif = &ahvif->deflink; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; + struct ieee80211_sta *sta = control->sta; + struct ath12k_link_vif *tmp_arvif; u32 info_flags = info->flags; + struct sk_buff *msdu_copied; + struct ath12k *ar, *tmp_ar; + struct ath12k_peer *peer; + unsigned long links_map; + bool is_mcast = false; + bool is_dvlan = false; + struct ethhdr *eth; bool is_prb_rsp; + u16 mcbc_gsn; + u8 link_id; int ret; + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ieee80211_free_txskb(hw, skb); + return; + } + + link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK); memset(skb_cb, 0, sizeof(*skb_cb)); skb_cb->vif = vif; @@ -5261,9 +7867,33 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, skb_cb->flags |= ATH12K_SKB_CIPHER_SET; } + /* handle only for MLO case, use deflink for non MLO case */ + if (ieee80211_vif_is_mld(vif)) { + link_id = ath12k_mac_get_tx_link(sta, vif, link_id, skb, info_flags); + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) { + ieee80211_free_txskb(hw, skb); + return; + } + } else { + link_id = 0; + } + + arvif = rcu_dereference(ahvif->link[link_id]); + if (!arvif || !arvif->ar) { + ath12k_warn(ahvif->ah, "failed to find arvif link id %u for frame transmission", + link_id); + ieee80211_free_txskb(hw, skb); + return; + } + + ar = arvif->ar; + skb_cb->link_id = link_id; is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { + eth = (struct ethhdr *)skb->data; + is_mcast = is_multicast_ether_addr(eth->h_dest); + skb_cb->flags |= ATH12K_SKB_HW_80211_ENCAP; } else if (ieee80211_is_mgmt(hdr->frame_control)) { ret = ath12k_mac_mgmt_tx(ar, skb, is_prb_rsp); @@ -5275,83 +7905,171 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, return; } + if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) + is_mcast = is_multicast_ether_addr(hdr->addr1); + /* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p) ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp); - ret = ath12k_dp_tx(ar, arvif, skb); - if (ret) { - ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); - ieee80211_free_txskb(hw, skb); + /* Checking if it is a DVLAN frame */ + if (!test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) && + !(skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) && + !(skb_cb->flags & ATH12K_SKB_CIPHER_SET) && + ieee80211_has_protected(hdr->frame_control)) + is_dvlan = true; + + if (!vif->valid_links || !is_mcast || is_dvlan || + test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { + ret = ath12k_dp_tx(ar, arvif, skb, false, 0, is_mcast); + if (unlikely(ret)) { + ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->ah->hw, skb); + return; + } + } else { + mcbc_gsn = atomic_inc_return(&ahvif->mcbc_gsn) & 0xfff; + + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, + IEEE80211_MLD_MAX_NUM_LINKS) { + tmp_arvif = rcu_dereference(ahvif->link[link_id]); + if (!tmp_arvif || !tmp_arvif->is_up) + continue; + + tmp_ar = tmp_arvif->ar; + msdu_copied = skb_copy(skb, GFP_ATOMIC); + if (!msdu_copied) { + ath12k_err(ar->ab, + "skb copy failure link_id 0x%X vdevid 0x%X\n", + link_id, tmp_arvif->vdev_id); + continue; + } + + ath12k_mlo_mcast_update_tx_link_address(vif, link_id, + msdu_copied, + info_flags); + + skb_cb = ATH12K_SKB_CB(msdu_copied); + skb_cb->link_id = link_id; + + /* For open mode, skip peer find logic */ + if (unlikely(!ahvif->key_cipher)) + goto skip_peer_find; + + spin_lock_bh(&tmp_ar->ab->base_lock); + peer = ath12k_peer_find_by_addr(tmp_ar->ab, tmp_arvif->bssid); + if (!peer) { + spin_unlock_bh(&tmp_ar->ab->base_lock); + ath12k_warn(tmp_ar->ab, + "failed to find peer for vdev_id 0x%X addr %pM link_map 0x%X\n", + tmp_arvif->vdev_id, tmp_arvif->bssid, + ahvif->links_map); + dev_kfree_skb_any(msdu_copied); + continue; + } + + key = peer->keys[peer->mcast_keyidx]; + if (key) { + skb_cb->cipher = key->cipher; + skb_cb->flags |= ATH12K_SKB_CIPHER_SET; + + hdr = (struct ieee80211_hdr *)msdu_copied->data; + if (!ieee80211_has_protected(hdr->frame_control)) + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_PROTECTED); + } + spin_unlock_bh(&tmp_ar->ab->base_lock); + +skip_peer_find: + ret = ath12k_dp_tx(tmp_ar, tmp_arvif, + msdu_copied, true, mcbc_gsn, is_mcast); + if (unlikely(ret)) { + if (ret == -ENOMEM) { + /* Drops are expected during heavy multicast + * frame flood. Print with debug log + * level to avoid lot of console prints + */ + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "failed to transmit frame %d\n", + ret); + } else { + ath12k_warn(ar->ab, + "failed to transmit frame %d\n", + ret); + } + + dev_kfree_skb_any(msdu_copied); + } + } + ieee80211_free_txskb(ar->ah->hw, skb); } } void ath12k_mac_drain_tx(struct ath12k *ar) { + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + /* make sure rcu-protected mac80211 tx path itself is drained */ synchronize_net(); - cancel_work_sync(&ar->wmi_mgmt_tx_work); + wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->wmi_mgmt_tx_work); ath12k_mgmt_over_wmi_tx_purge(ar); } static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) { - return -EOPNOTSUPP; - /* TODO: Need to support new monitor mode */ -} - -static void ath12k_mac_wait_reconfigure(struct ath12k_base *ab) -{ - int recovery_start_count; + struct htt_rx_ring_tlv_filter tlv_filter = {}; + struct ath12k_base *ab = ar->ab; + u32 ring_id, i; + int ret = 0; - if (!ab->is_reset) - return; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - recovery_start_count = atomic_inc_return(&ab->recovery_start_count); + if (!ab->hw_params->rxdma1_enable) + return ret; - ath12k_dbg(ab, ATH12K_DBG_MAC, "recovery start count %d\n", recovery_start_count); + if (enable) { + tlv_filter = ath12k_mac_mon_status_filter_default; - if (recovery_start_count == ab->num_radios) { - complete(&ab->recovery_start); - ath12k_dbg(ab, ATH12K_DBG_MAC, "recovery started success\n"); + if (ath12k_debugfs_rx_filter(ar)) + tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar); + } else { + tlv_filter.rxmon_disable = true; } - ath12k_dbg(ab, ATH12K_DBG_MAC, "waiting reconfigure...\n"); + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for monitor buf %d\n", + ret); + } + } - wait_for_completion_timeout(&ab->reconfigure_complete, - ATH12K_RECONFIGURE_TIMEOUT_HZ); + return ret; } static int ath12k_mac_start(struct ath12k *ar) { + struct ath12k_hw *ah = ar->ah; struct ath12k_base *ab = ar->ab; struct ath12k_pdev *pdev = ar->pdev; int ret; - mutex_lock(&ar->conf_mutex); - - switch (ar->state) { - case ATH12K_STATE_OFF: - ar->state = ATH12K_STATE_ON; - break; - case ATH12K_STATE_RESTARTING: - ar->state = ATH12K_STATE_RESTARTED; - ath12k_mac_wait_reconfigure(ab); - break; - case ATH12K_STATE_RESTARTED: - case ATH12K_STATE_WEDGED: - case ATH12K_STATE_ON: - WARN_ON(1); - ret = -EINVAL; - goto err; - } + lockdep_assert_held(&ah->hw_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1, pdev->pdev_id); if (ret) { - ath12k_err(ab, "failed to enable PMF QOS: (%d\n", ret); + ath12k_err(ab, "failed to enable PMF QOS: %d\n", ret); goto err; } @@ -5396,12 +8114,13 @@ static int ath12k_mac_start(struct ath12k *ar) /* TODO: Do we need to enable ANI? */ - ath12k_reg_update_chan_list(ar); + ath12k_reg_update_chan_list(ar, false); ar->num_started_vdevs = 0; ar->num_created_vdevs = 0; ar->num_peers = 0; ar->allocated_vdev_map = 0; + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; /* Configure monitor status ring with default rx_filter to get rx status * such as rssi, rx_duration. @@ -5430,36 +8149,80 @@ static int ath12k_mac_start(struct ath12k *ar) } } - mutex_unlock(&ar->conf_mutex); - rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], &ab->pdevs[ar->pdev_idx]); return 0; err: - ar->state = ATH12K_STATE_OFF; - mutex_unlock(&ar->conf_mutex); return ret; } +static void ath12k_drain_tx(struct ath12k_hw *ah) +{ + struct ath12k *ar = ah->radio; + int i; + + if (ath12k_ftm_mode) { + ath12k_err(ar->ab, "fail to start mac operations in ftm mode\n"); + return; + } + + lockdep_assert_wiphy(ah->hw->wiphy); + + for_each_ar(ah, ar, i) + ath12k_mac_drain_tx(ar); +} + static int ath12k_mac_op_start(struct ieee80211_hw *hw) { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar = ath12k_ah_to_ar(ah); - struct ath12k_base *ab = ar->ab; - int ret; + struct ath12k *ar; + int ret, i; - ath12k_mac_drain_tx(ar); + lockdep_assert_wiphy(hw->wiphy); - ret = ath12k_mac_start(ar); - if (ret) { - ath12k_err(ab, "fail to start mac operations in pdev idx %d ret %d\n", - ar->pdev_idx, ret); - return ret; + ath12k_drain_tx(ah); + + guard(mutex)(&ah->hw_mutex); + + switch (ah->state) { + case ATH12K_HW_STATE_OFF: + ah->state = ATH12K_HW_STATE_ON; + break; + case ATH12K_HW_STATE_RESTARTING: + ah->state = ATH12K_HW_STATE_RESTARTED; + break; + case ATH12K_HW_STATE_RESTARTED: + case ATH12K_HW_STATE_WEDGED: + case ATH12K_HW_STATE_ON: + case ATH12K_HW_STATE_TM: + ah->state = ATH12K_HW_STATE_OFF; + + WARN_ON(1); + return -EINVAL; + } + + for_each_ar(ah, ar, i) { + ret = ath12k_mac_start(ar); + if (ret) { + ah->state = ATH12K_HW_STATE_OFF; + + ath12k_err(ar->ab, "fail to start mac operations in pdev idx %d ret %d\n", + ar->pdev_idx, ret); + goto fail_start; + } } return 0; + +fail_start: + for (; i > 0; i--) { + ar = ath12k_ah_to_ar(ah, i - 1); + ath12k_mac_stop(ar); + } + + return ret; } int ath12k_mac_rfkill_config(struct ath12k *ar) @@ -5521,22 +8284,27 @@ int ath12k_mac_rfkill_enable_radio(struct ath12k *ar, bool enable) static void ath12k_mac_stop(struct ath12k *ar) { + struct ath12k_hw *ah = ar->ah; struct htt_ppdu_stats_info *ppdu_stats, *tmp; int ret; - mutex_lock(&ar->conf_mutex); + lockdep_assert_held(&ah->hw_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + ret = ath12k_mac_config_mon_status_default(ar, false); if (ret && (ret != -EOPNOTSUPP)) ath12k_err(ar->ab, "failed to clear rx_filter for monitor status ring: (%d)\n", ret); - clear_bit(ATH12K_CAC_RUNNING, &ar->dev_flags); - ar->state = ATH12K_STATE_OFF; - mutex_unlock(&ar->conf_mutex); + clear_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags); cancel_delayed_work_sync(&ar->scan.timeout); + wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.vdev_clean_wk); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->rfkill_work); + cancel_work_sync(&ar->ab->update_11d_work); + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); spin_lock_bh(&ar->data_lock); list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { @@ -5552,18 +8320,28 @@ static void ath12k_mac_stop(struct ath12k *ar) atomic_set(&ar->num_pending_mgmt_tx, 0); } -static void ath12k_mac_op_stop(struct ieee80211_hw *hw) +static void ath12k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar = ath12k_ah_to_ar(ah); + struct ath12k *ar; + int i; - ath12k_mac_drain_tx(ar); + lockdep_assert_wiphy(hw->wiphy); + + ath12k_drain_tx(ah); + + mutex_lock(&ah->hw_mutex); + + ah->state = ATH12K_HW_STATE_OFF; + + for_each_ar(ah, ar, i) + ath12k_mac_stop(ar); - ath12k_mac_stop(ar); + mutex_unlock(&ah->hw_mutex); } static u8 -ath12k_mac_get_vdev_stats_id(struct ath12k_vif *arvif) +ath12k_mac_get_vdev_stats_id(struct ath12k_link_vif *arvif) { struct ath12k_base *ab = arvif->ar->ab; u8 vdev_stats_id = 0; @@ -5585,32 +8363,97 @@ ath12k_mac_get_vdev_stats_id(struct ath12k_vif *arvif) return vdev_stats_id; } -static void ath12k_mac_setup_vdev_create_arg(struct ath12k_vif *arvif, - struct ath12k_wmi_vdev_create_arg *arg) +static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif, + u32 *flags, u32 *tx_vdev_id) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *link_conf; + struct ath12k *ar = arvif->ar; + struct ath12k_link_vif *tx_arvif; + + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in set mbssid params for vif %pM link %u\n", + ahvif->vif->addr, arvif->link_id); + return -ENOLINK; + } + + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (!tx_arvif) + return 0; + + if (link_conf->nontransmitted) { + if (ath12k_ar_to_hw(ar)->wiphy != + ath12k_ar_to_hw(tx_arvif->ar)->wiphy) + return -EINVAL; + + *flags = WMI_VDEV_MBSSID_FLAGS_NON_TRANSMIT_AP; + *tx_vdev_id = tx_arvif->vdev_id; + } else if (tx_arvif == arvif) { + *flags = WMI_VDEV_MBSSID_FLAGS_TRANSMIT_AP; + } else { + return -EINVAL; + } + + if (link_conf->ema_ap) + *flags |= WMI_VDEV_MBSSID_FLAGS_EMA_MODE; + + return 0; +} + +static int ath12k_mac_setup_vdev_create_arg(struct ath12k_link_vif *arvif, + struct ath12k_wmi_vdev_create_arg *arg) { struct ath12k *ar = arvif->ar; struct ath12k_pdev *pdev = ar->pdev; + struct ath12k_vif *ahvif = arvif->ahvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); arg->if_id = arvif->vdev_id; - arg->type = arvif->vdev_type; - arg->subtype = arvif->vdev_subtype; + arg->type = ahvif->vdev_type; + arg->subtype = ahvif->vdev_subtype; arg->pdev_id = pdev->pdev_id; - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { + arg->mbssid_flags = WMI_VDEV_MBSSID_FLAGS_NON_MBSSID_AP; + arg->mbssid_tx_vdev_id = 0; + if (!test_bit(WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT, + ar->ab->wmi_ab.svc_map)) { + ret = ath12k_mac_setup_vdev_params_mbssid(arvif, + &arg->mbssid_flags, + &arg->mbssid_tx_vdev_id); + if (ret) + return ret; + } + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { arg->chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; arg->chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; } - if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { arg->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; arg->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; } - if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP && + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { arg->chains[NL80211_BAND_6GHZ].tx = ar->num_tx_chains; arg->chains[NL80211_BAND_6GHZ].rx = ar->num_rx_chains; } arg->if_stats_id = ath12k_mac_get_vdev_stats_id(arvif); + + if (ath12k_mac_is_ml_arvif(arvif)) { + if (hweight16(ahvif->vif->valid_links) > ATH12K_WMI_MLO_MAX_LINKS) { + ath12k_warn(ar->ab, "too many MLO links during setting up vdev: %d", + ahvif->vif->valid_links); + return -EINVAL; + } + + ether_addr_copy(arg->mld_addr, ahvif->vif->addr); + } + + return 0; } static u32 @@ -5621,7 +8464,7 @@ ath12k_mac_prepare_he_mode(struct ath12k_pdev *pdev, u32 viftype) u32 *hecap_phy_ptr = NULL; u32 hemode; - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; else cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; @@ -5648,14 +8491,15 @@ ath12k_mac_prepare_he_mode(struct ath12k_pdev *pdev, u32 viftype) } static int ath12k_set_he_mu_sounding_mode(struct ath12k *ar, - struct ath12k_vif *arvif) + struct ath12k_link_vif *arvif) { u32 param_id, param_value; struct ath12k_base *ab = ar->ab; + struct ath12k_vif *ahvif = arvif->ahvif; int ret; param_id = WMI_VDEV_PARAM_SET_HEMU_MODE; - param_value = ath12k_mac_prepare_he_mode(ar->pdev, arvif->vif->type); + param_value = ath12k_mac_prepare_he_mode(ar->pdev, ahvif->vif->type); ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param_id, param_value); if (ret) { @@ -5678,9 +8522,10 @@ static int ath12k_set_he_mu_sounding_mode(struct ath12k *ar, return ret; } -static void ath12k_mac_update_vif_offload(struct ath12k_vif *arvif) +static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif) { - struct ieee80211_vif *vif = arvif->vif; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); struct ath12k *ar = arvif->ar; struct ath12k_base *ab = ar->ab; u32 param_id, param_value; @@ -5693,14 +8538,14 @@ static void ath12k_mac_update_vif_offload(struct ath12k_vif *arvif) IEEE80211_OFFLOAD_DECAP_ENABLED); if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) - arvif->tx_encap_type = ATH12K_HW_TXRX_ETHERNET; + ahvif->tx_encap_type = ATH12K_HW_TXRX_ETHERNET; else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) - arvif->tx_encap_type = ATH12K_HW_TXRX_RAW; + ahvif->tx_encap_type = ATH12K_HW_TXRX_RAW; else - arvif->tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI; + ahvif->tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI; ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, - param_id, arvif->tx_encap_type); + param_id, ahvif->tx_encap_type); if (ret) { ath12k_warn(ab, "failed to set vdev %d tx encap mode: %d\n", arvif->vdev_id, ret); @@ -5727,121 +8572,248 @@ static void ath12k_mac_update_vif_offload(struct ath12k_vif *arvif) static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + unsigned long links; + int link_id; - ath12k_mac_update_vif_offload(arvif); + lockdep_assert_wiphy(hw->wiphy); + + if (vif->valid_links) { + links = vif->valid_links; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (!(arvif && arvif->ar)) + continue; + + ath12k_mac_update_vif_offload(arvif); + } + + return; + } + + ath12k_mac_update_vif_offload(&ahvif->deflink); } -static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static bool ath12k_mac_vif_ap_active_any(struct ath12k_base *ab) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - struct ath12k_base *ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k_wmi_vdev_create_arg vdev_arg = {0}; - struct ath12k_wmi_peer_create_arg peer_param; - u32 param_id, param_value; - u16 nss; + struct ath12k_pdev *pdev; + struct ath12k_link_vif *arvif; int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up && + arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP) + return true; + } + } + return false; +} + +void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id) +{ + struct wmi_11d_scan_start_arg arg; int ret; - int bit; - vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; + if (ar->regdom_set_by_user) + goto fin; - mutex_lock(&ar->conf_mutex); + if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID) + goto fin; - if (vif->type == NL80211_IFTYPE_AP && - ar->num_peers > (ar->max_num_peers - 1)) { - ath12k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n"); - ret = -ENOBUFS; - goto err; + if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) + goto fin; + + if (ath12k_mac_vif_ap_active_any(ar->ab)) + goto fin; + + arg.vdev_id = vdev_id; + arg.start_interval_msec = 0; + arg.scan_period_msec = ATH12K_SCAN_11D_INTERVAL; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "mac start 11d scan for vdev %d\n", vdev_id); + + ret = ath12k_wmi_send_11d_scan_start_cmd(ar, &arg); + if (ret) { + ath12k_warn(ar->ab, "failed to start 11d scan vdev %d ret: %d\n", + vdev_id, ret); + } else { + ar->vdev_id_11d_scan = vdev_id; + if (ar->state_11d == ATH12K_11D_PREPARING) + ar->state_11d = ATH12K_11D_RUNNING; } - if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) { - ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n", - TARGET_NUM_VDEVS); - ret = -EBUSY; - goto err; +fin: + if (ar->state_11d == ATH12K_11D_PREPARING) { + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); } +} - memset(arvif, 0, sizeof(*arvif)); +void ath12k_mac_11d_scan_stop(struct ath12k *ar) +{ + int ret; + u32 vdev_id; - arvif->ar = ar; - arvif->vif = vif; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - INIT_LIST_HEAD(&arvif->list); + if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) + return; - /* Should we initialize any worker to handle connection loss indication - * from firmware in sta mode? - */ + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac stop 11d for vdev %d\n", + ar->vdev_id_11d_scan); - for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { - arvif->bitrate_mask.control[i].legacy = 0xffffffff; - memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].ht_mcs)); - memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + if (ar->state_11d == ATH12K_11D_PREPARING) { + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); } - bit = __ffs64(ab->free_vdev_map); + if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID) { + vdev_id = ar->vdev_id_11d_scan; - arvif->vdev_id = bit; - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + ret = ath12k_wmi_send_11d_scan_stop_cmd(ar, vdev_id); + if (ret) { + ath12k_warn(ar->ab, + "failed to stopt 11d scan vdev %d ret: %d\n", + vdev_id, ret); + } else { + ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID; + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); + } + } +} + +void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab) +{ + struct ath12k *ar; + struct ath12k_pdev *pdev; + int i; + + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac stop soc 11d scan\n"); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + ath12k_mac_11d_scan_stop(ar); + } +} + +static void ath12k_mac_determine_vdev_type(struct ieee80211_vif *vif, + struct ath12k_vif *ahvif) +{ + ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; switch (vif->type) { case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: - arvif->vdev_type = WMI_VDEV_TYPE_STA; + ahvif->vdev_type = WMI_VDEV_TYPE_STA; if (vif->p2p) - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT; + ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT; break; case NL80211_IFTYPE_MESH_POINT: - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; + ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; fallthrough; case NL80211_IFTYPE_AP: - arvif->vdev_type = WMI_VDEV_TYPE_AP; + ahvif->vdev_type = WMI_VDEV_TYPE_AP; if (vif->p2p) - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; + ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; break; case NL80211_IFTYPE_MONITOR: - arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; - ar->monitor_vdev_id = bit; + ahvif->vdev_type = WMI_VDEV_TYPE_MONITOR; break; case NL80211_IFTYPE_P2P_DEVICE: - arvif->vdev_type = WMI_VDEV_TYPE_STA; - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + ahvif->vdev_type = WMI_VDEV_TYPE_STA; + ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; break; default: WARN_ON(1); break; } +} + +int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) +{ + struct ath12k_hw *ah = ar->ah; + struct ath12k_base *ab = ar->ab; + struct ieee80211_hw *hw = ah->hw; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ath12k_wmi_vdev_create_arg vdev_arg = {0}; + struct ath12k_wmi_peer_create_arg peer_param = {0}; + struct ieee80211_bss_conf *link_conf = NULL; + u32 param_id, param_value; + u16 nss; + int i; + int ret, vdev_id; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + /* In NO_VIRTUAL_MONITOR, its necessary to restrict only one monitor + * interface in each radio + */ + if (vif->type == NL80211_IFTYPE_MONITOR && ar->monitor_vdev_created) + return -EINVAL; + + link_id = arvif->link_id; + + if (link_id < IEEE80211_MLD_MAX_NUM_LINKS) { + link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in vdev create for vif %pM link %u\n", + vif->addr, arvif->link_id); + return -ENOLINK; + } + } + + if (link_conf) + memcpy(arvif->bssid, link_conf->addr, ETH_ALEN); + else + memcpy(arvif->bssid, vif->addr, ETH_ALEN); + + arvif->ar = ar; + vdev_id = __ffs64(ab->free_vdev_map); + arvif->vdev_id = vdev_id; + if (vif->type == NL80211_IFTYPE_MONITOR) + ar->monitor_vdev_id = vdev_id; - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac add interface id %d type %d subtype %d map %llx\n", - arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev create id %d type %d subtype %d map %llx\n", + arvif->vdev_id, ahvif->vdev_type, ahvif->vdev_subtype, ab->free_vdev_map); vif->cab_queue = arvif->vdev_id % (ATH12K_HW_MAX_QUEUES - 1); for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++) vif->hw_queue[i] = i % (ATH12K_HW_MAX_QUEUES - 1); - ath12k_mac_setup_vdev_create_arg(arvif, &vdev_arg); + ret = ath12k_mac_setup_vdev_create_arg(arvif, &vdev_arg); + if (ret) { + ath12k_warn(ab, "failed to create vdev parameters %d: %d\n", + arvif->vdev_id, ret); + goto err; + } - ret = ath12k_wmi_vdev_create(ar, vif->addr, &vdev_arg); + ret = ath12k_wmi_vdev_create(ar, arvif->bssid, &vdev_arg); if (ret) { ath12k_warn(ab, "failed to create WMI vdev %d: %d\n", arvif->vdev_id, ret); - goto err; + return ret; } ar->num_created_vdevs++; + arvif->is_created = true; ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM created, vdev_id %d\n", vif->addr, arvif->vdev_id); ar->allocated_vdev_map |= 1LL << arvif->vdev_id; @@ -5862,10 +8834,10 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, goto err_vdev_del; } - switch (arvif->vdev_type) { + switch (ahvif->vdev_type) { case WMI_VDEV_TYPE_AP: peer_param.vdev_id = arvif->vdev_id; - peer_param.peer_addr = vif->addr; + peer_param.peer_addr = arvif->bssid; peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; ret = ath12k_peer_create(ar, arvif, NULL, &peer_param); if (ret) { @@ -5880,6 +8852,7 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); goto err_peer_del; } + ath12k_mac_11d_scan_stop_all(ar->ab); break; case WMI_VDEV_TYPE_STA: param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY; @@ -5918,12 +8891,26 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); goto err_peer_del; } + + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { + reinit_completion(&ar->completed_11d_scan); + ar->state_11d = ATH12K_11D_PREPARING; + } + break; + case WMI_VDEV_TYPE_MONITOR: + ar->monitor_vdev_created = true; break; default: break; } - arvif->txpower = vif->bss_conf.txpower; + if (link_conf) + arvif->txpower = link_conf->txpower; + else + arvif->txpower = NL80211_TX_POWER_AUTOMATIC; + ret = ath12k_mac_txpower_recalc(ar); if (ret) goto err_peer_del; @@ -5939,36 +8926,38 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, ath12k_dp_vdev_tx_attach(ar, arvif); - if (vif->type != NL80211_IFTYPE_MONITOR && ar->monitor_conf_enabled) - ath12k_mac_monitor_vdev_create(ar); - - mutex_unlock(&ar->conf_mutex); - return ret; err_peer_del: - if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { reinit_completion(&ar->peer_delete_done); - ret = ath12k_wmi_send_peer_delete_cmd(ar, vif->addr, + ret = ath12k_wmi_send_peer_delete_cmd(ar, arvif->bssid, arvif->vdev_id); if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", - arvif->vdev_id, vif->addr); + arvif->vdev_id, arvif->bssid); goto err; } ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, - vif->addr); + arvif->bssid); if (ret) - goto err; + goto err_vdev_del; ar->num_peers--; } err_vdev_del: + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ar->monitor_vdev_id = -1; + ar->monitor_vdev_created = false; + } + ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; + arvif->is_created = false; + arvif->ar = NULL; ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); @@ -5977,11 +8966,232 @@ err_vdev_del: spin_unlock_bh(&ar->data_lock); err: - mutex_unlock(&ar->conf_mutex); - + arvif->ar = NULL; return ret; } +static void ath12k_mac_vif_flush_key_cache(struct ath12k_link_vif *arvif) +{ + struct ath12k_key_conf *key_conf, *tmp; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = ahvif->ah; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + struct ath12k_vif_cache *cache = ahvif->cache[arvif->link_id]; + int ret; + + lockdep_assert_wiphy(ah->hw->wiphy); + + list_for_each_entry_safe(key_conf, tmp, &cache->key_conf.list, list) { + arsta = NULL; + if (key_conf->sta) { + ahsta = ath12k_sta_to_ahsta(key_conf->sta); + arsta = wiphy_dereference(ah->hw->wiphy, + ahsta->link[arvif->link_id]); + if (!arsta) + goto free_cache; + } + + ret = ath12k_mac_set_key(arvif->ar, key_conf->cmd, + arvif, arsta, + key_conf->key); + if (ret) + ath12k_warn(arvif->ar->ab, "unable to apply set key param to vdev %d ret %d\n", + arvif->vdev_id, ret); +free_cache: + list_del(&key_conf->list); + kfree(key_conf); + } +} + +static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ath12k_vif_cache *cache = ahvif->cache[arvif->link_id]; + struct ath12k_base *ab = ar->ab; + struct ieee80211_bss_conf *link_conf; + + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (!cache) + return; + + if (cache->tx_conf.changed) { + ret = ath12k_mac_conf_tx(arvif, cache->tx_conf.ac, + &cache->tx_conf.tx_queue_params); + if (ret) + ath12k_warn(ab, + "unable to apply tx config parameters to vdev %d\n", + ret); + } + + if (cache->bss_conf_changed) { + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in cache flush for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } + ath12k_mac_bss_info_changed(ar, arvif, link_conf, + cache->bss_conf_changed); + } + + if (!list_empty(&cache->key_conf.list)) + ath12k_mac_vif_flush_key_cache(arvif); + + ath12k_ahvif_put_link_cache(ahvif, arvif->link_id); +} + +static struct ath12k *ath12k_mac_assign_vif_to_vdev(struct ieee80211_hw *hw, + struct ath12k_link_vif *arvif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ath12k_link_vif *scan_arvif; + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar; + struct ath12k_base *ab; + u8 link_id = arvif->link_id; + int ret; + + lockdep_assert_wiphy(hw->wiphy); + + if (ah->num_radio == 1) + ar = ah->radio; + else if (ctx) + ar = ath12k_get_ar_by_ctx(hw, ctx); + else + return NULL; + + if (!ar) + return NULL; + + /* cleanup the scan vdev if we are done scan on that ar + * and now we want to create for actual usage. + */ + if (ieee80211_vif_is_mld(vif)) { + scan_arvif = wiphy_dereference(hw->wiphy, + ahvif->link[ATH12K_DEFAULT_SCAN_LINK]); + if (scan_arvif && scan_arvif->ar == ar) { + ar->scan.arvif = NULL; + ath12k_mac_remove_link_interface(hw, scan_arvif); + ath12k_mac_unassign_link_vif(scan_arvif); + } + } + + if (arvif->ar) { + /* This is not expected really */ + if (WARN_ON(!arvif->is_created)) { + arvif->ar = NULL; + return NULL; + } + + if (ah->num_radio == 1) + return arvif->ar; + + /* This can happen as scan vdev gets created during multiple scans + * across different radios before a vdev is brought up in + * a certain radio. + */ + if (ar != arvif->ar) { + if (WARN_ON(arvif->is_started)) + return NULL; + + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } + } + + ab = ar->ab; + + /* Assign arvif again here since previous radio switch block + * would've unassigned and cleared it. + */ + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + if (vif->type == NL80211_IFTYPE_AP && + ar->num_peers > (ar->max_num_peers - 1)) { + ath12k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n"); + goto unlock; + } + + if (arvif->is_created) + goto flush; + + if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) { + ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n", + TARGET_NUM_VDEVS); + goto unlock; + } + + ret = ath12k_mac_vdev_create(ar, arvif); + if (ret) { + ath12k_warn(ab, "failed to create vdev %pM ret %d", vif->addr, ret); + goto unlock; + } + +flush: + /* If the vdev is created during channel assign and not during + * add_interface(), Apply any parameters for the vdev which were received + * after add_interface, corresponding to this vif. + */ + ath12k_mac_vif_cache_flush(ar, arvif); +unlock: + return arvif->ar; +} + +static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_reg_info *reg_info; + struct ath12k_link_vif *arvif; + struct ath12k_base *ab; + struct ath12k *ar; + int i; + + lockdep_assert_wiphy(hw->wiphy); + + memset(ahvif, 0, sizeof(*ahvif)); + + ahvif->ah = ah; + ahvif->vif = vif; + arvif = &ahvif->deflink; + + ath12k_mac_init_arvif(ahvif, arvif, -1); + + /* Allocate Default Queue now and reassign during actual vdev create */ + vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE; + for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++) + vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE; + + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + + ath12k_mac_determine_vdev_type(vif, ahvif); + + for_each_ar(ah, ar, i) { + if (!ath12k_wmi_supports_6ghz_cc_ext(ar)) + continue; + + ab = ar->ab; + reg_info = ab->reg_info[ar->pdev_idx]; + ath12k_dbg(ab, ATH12K_DBG_MAC, "interface added to change reg rules\n"); + ah->regd_updated = false; + ath12k_reg_handle_chan_list(ab, reg_info, ahvif->vdev_type, + IEEE80211_REG_UNSET_AP); + break; + } + + /* Defer vdev creation until assign_chanctx or hw_scan is initiated as driver + * will not know if this interface is an ML vif at this point. + */ + return 0; +} + static void ath12k_mac_vif_unref(struct ath12k_dp *dp, struct ieee80211_vif *vif) { struct ath12k_tx_desc_info *tx_desc_info; @@ -6007,30 +9217,15 @@ static void ath12k_mac_vif_unref(struct ath12k_dp *dp, struct ieee80211_vif *vif } } -static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ath12k_link_vif *arvif) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k_base *ab; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ath12k_base *ab = ar->ab; unsigned long time_left; int ret; - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; - - mutex_lock(&ar->conf_mutex); - - ath12k_dbg(ab, ATH12K_DBG_MAC, "mac remove interface (vdev %d)\n", - arvif->vdev_id); - - if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { - ret = ath12k_peer_delete(ar, arvif->vdev_id, vif->addr); - if (ret) - ath12k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n", - arvif->vdev_id, ret); - } + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); reinit_completion(&ar->vdev_delete_done); @@ -6048,18 +9243,15 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, goto err_vdev_del; } - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ab->free_vdev_map |= 1LL << arvif->vdev_id; + ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); + ar->num_created_vdevs--; + + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ar->monitor_vdev_id = -1; ar->monitor_vdev_created = false; - } else if (ar->monitor_vdev_created && !ar->monitor_started) { - ret = ath12k_mac_monitor_vdev_delete(ar); } - ab->free_vdev_map |= 1LL << (arvif->vdev_id); - ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); - ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); - ar->num_created_vdevs--; - ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", vif->addr, arvif->vdev_id); @@ -6069,6 +9261,7 @@ err_vdev_del: spin_unlock_bh(&ar->data_lock); ath12k_peer_cleanup(ar, arvif->vdev_id); + ath12k_ahvif_put_link_cache(ahvif, arvif->link_id); idr_for_each(&ar->txmgmt_idr, ath12k_mac_vif_txmgmt_idr_remove, vif); @@ -6078,11 +9271,61 @@ err_vdev_del: /* Recalc txpower for remaining vdev */ ath12k_mac_txpower_recalc(ar); - clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); /* TODO: recal traffic pause state based on the available vdevs */ + arvif->is_created = false; + arvif->ar = NULL; - mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + struct ath12k *ar; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + for (link_id = 0; link_id < ATH12K_NUM_MAX_LINKS; link_id++) { + /* if we cached some config but never received assign chanctx, + * free the allocated cache. + */ + ath12k_ahvif_put_link_cache(ahvif, link_id); + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (!arvif || !arvif->is_created) + continue; + + ar = arvif->ar; + + /* Scan abortion is in progress since before this, cancel_hw_scan() + * is expected to be executed. Since link is anyways going to be removed + * now, just cancel the worker and send the scan aborted to user space + */ + if (ar->scan.arvif == arvif) { + wiphy_work_cancel(hw->wiphy, &ar->scan.vdev_clean_wk); + + spin_lock_bh(&ar->data_lock); + ar->scan.arvif = NULL; + if (!ar->scan.is_roc) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(ar->ah->hw, &info); + } + + ar->scan.state = ATH12K_SCAN_IDLE; + ar->scan_channel = NULL; + ar->scan.roc_freq = 0; + spin_unlock_bh(&ar->data_lock); + } + + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } } /* FIXME: Has to be verified. */ @@ -6095,35 +9338,6 @@ err_vdev_del: FIF_PROBE_REQ | \ FIF_FCSFAIL) -static void ath12k_mac_configure_filter(struct ath12k *ar, - unsigned int total_flags) -{ - bool reset_flag; - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - ar->filter_flags = total_flags; - - /* For monitor mode */ - reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); - - ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag); - if (!ret) { - if (!reset_flag) - set_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); - else - clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); - } else { - ath12k_warn(ar->ab, - "fail to set monitor filter: %d\n", ret); - } - - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "total_flags:0x%x, reset_flag:%d\n", - total_flags, reset_flag); -} - static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -6132,29 +9346,30 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); + ar = ath12k_ah_to_ar(ah, 0); *total_flags &= SUPPORTED_FILTERS; - ath12k_mac_configure_filter(ar, *total_flags); - - mutex_unlock(&ar->conf_mutex); + ar->filter_flags = *total_flags; } static int ath12k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + int antennas_rx = 0, antennas_tx = 0; struct ath12k *ar; + int i; - ar = ath12k_ah_to_ar(ah); - - mutex_lock(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); - *tx_ant = ar->cfg_tx_chainmask; - *rx_ant = ar->cfg_rx_chainmask; + for_each_ar(ah, ar, i) { + antennas_rx = max_t(u32, antennas_rx, ar->cfg_rx_chainmask); + antennas_tx = max_t(u32, antennas_tx, ar->cfg_tx_chainmask); + } - mutex_unlock(&ar->conf_mutex); + *tx_ant = antennas_tx; + *rx_ant = antennas_rx; return 0; } @@ -6163,31 +9378,40 @@ static int ath12k_mac_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - int ret; + int ret = 0; + int i; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); - ret = __ath12k_set_antenna(ar, tx_ant, rx_ant); - mutex_unlock(&ar->conf_mutex); + for_each_ar(ah, ar, i) { + ret = __ath12k_set_antenna(ar, tx_ant, rx_ant); + if (ret) + break; + } return ret; } -static int ath12k_mac_ampdu_action(struct ath12k_vif *arvif, - struct ieee80211_ampdu_params *params) +static int ath12k_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params, + u8 link_id) { - struct ath12k *ar = arvif->ar; + struct ath12k *ar; int ret = -EINVAL; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_get_ar_by_vif(hw, vif, link_id); + if (!ar) + return -EINVAL; switch (params->action) { case IEEE80211_AMPDU_RX_START: - ret = ath12k_dp_rx_ampdu_start(ar, params); + ret = ath12k_dp_rx_ampdu_start(ar, params, link_id); break; case IEEE80211_AMPDU_RX_STOP: - ret = ath12k_dp_rx_ampdu_stop(ar, params); + ret = ath12k_dp_rx_ampdu_stop(ar, params, link_id); break; case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_STOP_CONT: @@ -6201,6 +9425,10 @@ static int ath12k_mac_ampdu_action(struct ath12k_vif *arvif, break; } + if (ret) + ath12k_warn(ar->ab, "unable to perform ampdu action %d for vif %pM link %u ret %d\n", + params->action, vif->addr, link_id, ret); + return ret; } @@ -6208,48 +9436,51 @@ static int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_sta *sta = params->sta; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + unsigned long links_map = ahsta->links_map; int ret = -EINVAL; + u8 link_id; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); - ret = ath12k_mac_ampdu_action(arvif, params); - mutex_unlock(&ar->conf_mutex); + if (WARN_ON(!links_map)) + return ret; - if (ret) - ath12k_warn(ar->ab, "pdev idx %d unable to perform ampdu action %d ret %d\n", - ar->pdev_idx, params->action, ret); + for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) { + ret = ath12k_mac_ampdu_action(hw, vif, params, link_id); + if (ret) + return ret; + } - return ret; + return 0; } static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ath12k_base *ab; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_get_ar_by_ctx(hw, ctx); + if (!ar) + return -EINVAL; + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, - "mac chanctx add freq %u width %d ptr %pK\n", + "mac chanctx add freq %u width %d ptr %p\n", ctx->def.chan->center_freq, ctx->def.width, ctx); - mutex_lock(&ar->conf_mutex); - spin_lock_bh(&ar->data_lock); /* TODO: In case of multiple channel context, populate rx_channel from * Rx PPDU desc information. */ ar->rx_channel = ctx->def.chan; spin_unlock_bh(&ar->data_lock); - - mutex_unlock(&ar->conf_mutex); + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; return 0; } @@ -6257,27 +9488,28 @@ static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw, static void ath12k_mac_op_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ath12k_base *ab; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_get_ar_by_ctx(hw, ctx); + if (!ar) + return; + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, - "mac chanctx remove freq %u width %d ptr %pK\n", + "mac chanctx remove freq %u width %d ptr %p\n", ctx->def.chan->center_freq, ctx->def.width, ctx); - mutex_lock(&ar->conf_mutex); - spin_lock_bh(&ar->data_lock); /* TODO: In case of there is one more channel context left, populate * rx_channel with the channel of that remaining channel context. */ ar->rx_channel = NULL; spin_unlock_bh(&ar->data_lock); - - mutex_unlock(&ar->conf_mutex); + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; } static enum wmi_phy_mode @@ -6286,14 +9518,24 @@ ath12k_mac_check_down_grade_phy_mode(struct ath12k *ar, enum nl80211_band band, enum nl80211_iftype type) { - struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_sta_eht_cap *eht_cap = NULL; enum wmi_phy_mode down_mode; + int n = ar->mac.sbands[band].n_iftype_data; + int i; + struct ieee80211_sband_iftype_data *data; if (mode < MODE_11BE_EHT20) return mode; - eht_cap = &ar->mac.iftype[band][type].eht_cap; - if (eht_cap->has_eht) + data = ar->mac.iftype[band]; + for (i = 0; i < n; i++) { + if (data[i].types_mask & BIT(type)) { + eht_cap = &data[i].eht_cap; + break; + } + } + + if (eht_cap && eht_cap->has_eht) return mode; switch (mode) { @@ -6333,8 +9575,66 @@ ath12k_mac_check_down_grade_phy_mode(struct ath12k *ar, return down_mode; } +static void +ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, + struct wmi_ml_arg *ml_arg) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct wmi_ml_partner_info *partner_info; + struct ieee80211_bss_conf *link_conf; + struct ath12k_link_vif *arvif_p; + unsigned long links; + u8 link_id; + + lockdep_assert_wiphy(ahvif->ah->hw->wiphy); + + if (!ath12k_mac_is_ml_arvif(arvif)) + return; + + if (hweight16(ahvif->vif->valid_links) > ATH12K_WMI_MLO_MAX_LINKS) + return; + + ml_arg->enabled = true; + + /* Driver always add a new link via VDEV START, FW takes + * care of internally adding this link to existing + * link vdevs which are advertised as partners below + */ + ml_arg->link_add = true; + + ml_arg->assoc_link = arvif->is_sta_assoc_link; + + partner_info = ml_arg->partner_info; + + links = ahvif->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif_p = wiphy_dereference(ahvif->ah->hw->wiphy, ahvif->link[link_id]); + + if (WARN_ON(!arvif_p)) + continue; + + if (arvif == arvif_p) + continue; + + if (!arvif_p->is_created) + continue; + + link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, + ahvif->vif->link_conf[arvif_p->link_id]); + + if (!link_conf) + continue; + + partner_info->vdev_id = arvif_p->vdev_id; + partner_info->hw_link_id = arvif_p->ar->pdev->hw_link_id; + ether_addr_copy(partner_info->addr, link_conf->addr); + ml_arg->num_partner_links++; + partner_info++; + } +} + static int -ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, +ath12k_mac_vdev_start_restart(struct ath12k_link_vif *arvif, struct ieee80211_chanctx_conf *ctx, bool restart) { @@ -6342,10 +9642,20 @@ ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, struct ath12k_base *ab = ar->ab; struct wmi_vdev_start_req_arg arg = {}; const struct cfg80211_chan_def *chandef = &ctx->def; - int he_support = arvif->vif->bss_conf.he_support; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *link_conf; + unsigned int dfs_cac_time; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); + + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in vdev start for vif %pM link %u\n", + ahvif->vif->addr, arvif->link_id); + return -ENOLINK; + } reinit_completion(&ar->vdev_setup_done); @@ -6361,24 +9671,30 @@ ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, arg.mode = ath12k_mac_check_down_grade_phy_mode(ar, arg.mode, chandef->chan->band, - arvif->vif->type); + ahvif->vif->type); arg.min_power = 0; - arg.max_power = chandef->chan->max_power * 2; - arg.max_reg_power = chandef->chan->max_reg_power * 2; - arg.max_antenna_gain = chandef->chan->max_antenna_gain * 2; + arg.max_power = chandef->chan->max_power; + arg.max_reg_power = chandef->chan->max_reg_power; + arg.max_antenna_gain = chandef->chan->max_antenna_gain; arg.pref_tx_streams = ar->num_tx_chains; arg.pref_rx_streams = ar->num_rx_chains; - /* Fill the MBSSID flags to indicate AP is non MBSSID by default - * Corresponding flags would be updated with MBSSID support. - */ arg.mbssid_flags = WMI_VDEV_MBSSID_FLAGS_NON_MBSSID_AP; + arg.mbssid_tx_vdev_id = 0; + if (test_bit(WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT, + ar->ab->wmi_ab.svc_map)) { + ret = ath12k_mac_setup_vdev_params_mbssid(arvif, + &arg.mbssid_flags, + &arg.mbssid_tx_vdev_id); + if (ret) + return ret; + } - if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { - arg.ssid = arvif->u.ap.ssid; - arg.ssid_len = arvif->u.ap.ssid_len; - arg.hidden_ssid = arvif->u.ap.hidden_ssid; + if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = ahvif->u.ap.ssid; + arg.ssid_len = ahvif->u.ap.ssid_len; + arg.hidden_ssid = ahvif->u.ap.hidden_ssid; /* For now allow DFS for AP mode */ arg.chan_radar = !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); @@ -6392,7 +9708,7 @@ ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, spin_unlock_bh(&ab->base_lock); /* TODO: Notify if secondary 80Mhz also needs radar detection */ - if (he_support) { + if (link_conf->he_support) { ret = ath12k_set_he_mu_sounding_mode(ar, arvif); if (ret) { ath12k_warn(ar->ab, "failed to set he mode vdev %i\n", @@ -6404,6 +9720,9 @@ ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, arg.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); + if (!restart) + ath12k_mac_mlo_get_vdev_args(arvif, &arg.ml); + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac vdev %d start center_freq %d phymode %s punct_bitmap 0x%x\n", arg.vdev_id, arg.freq, @@ -6423,24 +9742,33 @@ ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, return ret; } + /* TODO: For now we only set TPC power here. However when + * channel changes, say CSA, it should be updated again. + */ + if (ath12k_mac_supports_station_tpc(ar, ahvif, chandef)) { + ath12k_mac_fill_reg_tpc_info(ar, arvif, ctx); + ath12k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id, + &arvif->reg_tpc_info); + } + ar->num_started_vdevs++; ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM started, vdev_id %d\n", - arvif->vif->addr, arvif->vdev_id); + ahvif->vif->addr, arvif->vdev_id); - /* Enable CAC Flag in the driver by checking the channel DFS cac time, - * i.e dfs_cac_ms value which will be valid only for radar channels - * and state as NL80211_DFS_USABLE which indicates CAC needs to be - * done before channel usage. This flags is used to drop rx packets. + /* Enable CAC Running Flag in the driver by checking all sub-channel's DFS + * state as NL80211_DFS_USABLE which indicates CAC needs to be + * done before channel usage. This flag is used to drop rx packets. * during CAC. */ /* TODO: Set the flag for other interface types as required */ - if (arvif->vdev_type == WMI_VDEV_TYPE_AP && - chandef->chan->dfs_cac_ms && - chandef->chan->dfs_state == NL80211_DFS_USABLE) { - set_bit(ATH12K_CAC_RUNNING, &ar->dev_flags); + if (arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP && ctx->radar_enabled && + cfg80211_chandef_dfs_usable(hw->wiphy, chandef)) { + set_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags); + dfs_cac_time = cfg80211_chandef_dfs_cac_time(hw->wiphy, chandef); + ath12k_dbg(ab, ATH12K_DBG_MAC, - "CAC Started in chan_freq %d for vdev %d\n", - arg.freq, arg.vdev_id); + "CAC started dfs_cac_time %u center_freq %d center_freq1 %d for vdev %d\n", + dfs_cac_time, arg.freq, arg.band_center_freq1, arg.vdev_id); } ret = ath12k_mac_set_txbf_conf(arvif); @@ -6451,13 +9779,13 @@ ath12k_mac_vdev_start_restart(struct ath12k_vif *arvif, return 0; } -static int ath12k_mac_vdev_start(struct ath12k_vif *arvif, +static int ath12k_mac_vdev_start(struct ath12k_link_vif *arvif, struct ieee80211_chanctx_conf *ctx) { return ath12k_mac_vdev_start_restart(arvif, ctx, false); } -static int ath12k_mac_vdev_restart(struct ath12k_vif *arvif, +static int ath12k_mac_vdev_restart(struct ath12k_link_vif *arvif, struct ieee80211_chanctx_conf *ctx) { return ath12k_mac_vdev_start_restart(arvif, ctx, true); @@ -6468,38 +9796,135 @@ struct ath12k_mac_change_chanctx_arg { struct ieee80211_vif_chanctx_switch *vifs; int n_vifs; int next_vif; + struct ath12k *ar; }; static void ath12k_mac_change_chanctx_cnt_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_mac_change_chanctx_arg *arg = data; + struct ieee80211_bss_conf *link_conf; + struct ath12k_link_vif *arvif; + unsigned long links_map; + u8 link_id; - if (rcu_access_pointer(vif->bss_conf.chanctx_conf) != arg->ctx) - return; + lockdep_assert_wiphy(ahvif->ah->hw->wiphy); + + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(ahvif->ah->hw->wiphy, ahvif->link[link_id]); + if (WARN_ON(!arvif)) + continue; + + if (arvif->ar != arg->ar) + continue; + + link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, + vif->link_conf[link_id]); + if (WARN_ON(!link_conf)) + continue; - arg->n_vifs++; + if (rcu_access_pointer(link_conf->chanctx_conf) != arg->ctx) + continue; + + arg->n_vifs++; + } } static void ath12k_mac_change_chanctx_fill_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_mac_change_chanctx_arg *arg = data; + struct ieee80211_bss_conf *link_conf; struct ieee80211_chanctx_conf *ctx; + struct ath12k_link_vif *arvif; + unsigned long links_map; + u8 link_id; - ctx = rcu_access_pointer(vif->bss_conf.chanctx_conf); - if (ctx != arg->ctx) - return; + lockdep_assert_wiphy(ahvif->ah->hw->wiphy); - if (WARN_ON(arg->next_vif == arg->n_vifs)) - return; + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(ahvif->ah->hw->wiphy, ahvif->link[link_id]); + if (WARN_ON(!arvif)) + continue; + + if (arvif->ar != arg->ar) + continue; + + link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, + vif->link_conf[arvif->link_id]); + if (WARN_ON(!link_conf)) + continue; - arg->vifs[arg->next_vif].vif = vif; - arg->vifs[arg->next_vif].old_ctx = ctx; - arg->vifs[arg->next_vif].new_ctx = ctx; - arg->next_vif++; + ctx = rcu_access_pointer(link_conf->chanctx_conf); + if (ctx != arg->ctx) + continue; + + if (WARN_ON(arg->next_vif == arg->n_vifs)) + return; + + arg->vifs[arg->next_vif].vif = vif; + arg->vifs[arg->next_vif].old_ctx = ctx; + arg->vifs[arg->next_vif].new_ctx = ctx; + arg->vifs[arg->next_vif].link_conf = link_conf; + arg->next_vif++; + } +} + +static u32 ath12k_mac_nlwidth_to_wmiwidth(enum nl80211_chan_width width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20: + return WMI_CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return WMI_CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return WMI_CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_160: + return WMI_CHAN_WIDTH_160; + case NL80211_CHAN_WIDTH_80P80: + return WMI_CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_5: + return WMI_CHAN_WIDTH_5; + case NL80211_CHAN_WIDTH_10: + return WMI_CHAN_WIDTH_10; + case NL80211_CHAN_WIDTH_320: + return WMI_CHAN_WIDTH_320; + default: + WARN_ON(1); + return WMI_CHAN_WIDTH_20; + } +} + +static int ath12k_mac_update_peer_puncturing_width(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct cfg80211_chan_def def) +{ + u32 param_id, param_value; + int ret; + + if (arvif->ahvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + param_id = WMI_PEER_CHWIDTH_PUNCTURE_20MHZ_BITMAP; + param_value = ath12k_mac_nlwidth_to_wmiwidth(def.width) | + u32_encode_bits((~def.punctured), + WMI_PEER_PUNCTURE_BITMAP); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "punctured bitmap %02x width %d vdev %d\n", + def.punctured, def.width, arvif->vdev_id); + + ret = ath12k_wmi_set_peer_param(ar, arvif->bssid, + arvif->vdev_id, param_id, + param_value); + + return ret; } static void @@ -6507,19 +9932,31 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, struct ieee80211_vif_chanctx_switch *vifs, int n_vifs) { + struct ath12k_wmi_vdev_up_params params = {}; + struct ath12k_link_vif *arvif, *tx_arvif; + struct ieee80211_bss_conf *link_conf; struct ath12k_base *ab = ar->ab; - struct ath12k_vif *arvif; + struct ieee80211_vif *vif; + struct ath12k_vif *ahvif; + u8 link_id; int ret; int i; bool monitor_vif = false; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); for (i = 0; i < n_vifs; i++) { - arvif = ath12k_vif_to_arvif(vifs[i].vif); - - if (vifs[i].vif->type == NL80211_IFTYPE_MONITOR) + vif = vifs[i].vif; + ahvif = ath12k_vif_to_ahvif(vif); + link_conf = vifs[i].link_conf; + link_id = link_conf->link_id; + arvif = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + ahvif->link[link_id]); + + if (vif->type == NL80211_IFTYPE_MONITOR) { monitor_vif = true; + continue; + } ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", @@ -6532,29 +9969,6 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, if (WARN_ON(!arvif->is_started)) continue; - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath12k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) { - ath12k_warn(ab, "failed to down vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - /* All relevant vdevs are downed and associated channel resources - * should be available for the channel switch now. - */ - - /* TODO: Update ar->rx_channel */ - - for (i = 0; i < n_vifs; i++) { - arvif = ath12k_vif_to_arvif(vifs[i].vif); - - if (WARN_ON(!arvif->is_started)) - continue; - arvif->punct_bitmap = vifs[i].new_ctx->def.punctured; /* Firmware expect vdev_restart only if vdev is up. @@ -6587,13 +10001,33 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, ath12k_warn(ab, "failed to update bcn tmpl during csa: %d\n", ret); - ret = ath12k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, - arvif->bssid); + memset(¶ms, 0, sizeof(params)); + params.vdev_id = arvif->vdev_id; + params.aid = ahvif->aid; + params.bssid = arvif->bssid; + + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (tx_arvif) { + params.tx_bssid = tx_arvif->bssid; + params.nontx_profile_idx = link_conf->bssid_index; + params.nontx_profile_cnt = 1 << link_conf->bssid_indicator; + } + ret = ath12k_wmi_vdev_up(arvif->ar, ¶ms); if (ret) { ath12k_warn(ab, "failed to bring vdev up %d: %d\n", arvif->vdev_id, ret); continue; } + + ret = ath12k_mac_update_peer_puncturing_width(arvif->ar, arvif, + vifs[i].new_ctx->def); + if (ret) { + ath12k_warn(ar->ab, + "failed to update puncturing bitmap %02x and width %d: %d\n", + vifs[i].new_ctx->def.punctured, + vifs[i].new_ctx->def.width, ret); + continue; + } } /* Restart the internal monitor vdev on new channel */ @@ -6607,10 +10041,10 @@ static void ath12k_mac_update_active_vif_chan(struct ath12k *ar, struct ieee80211_chanctx_conf *ctx) { - struct ath12k_mac_change_chanctx_arg arg = { .ctx = ctx }; + struct ath12k_mac_change_chanctx_arg arg = { .ctx = ctx, .ar = ar }; struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_NORMAL, @@ -6637,24 +10071,26 @@ static void ath12k_mac_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ath12k_base *ab; - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); + ar = ath12k_get_ar_by_ctx(hw, ctx); + if (!ar) + return; + + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, - "mac chanctx change freq %u width %d ptr %pK changed %x\n", + "mac chanctx change freq %u width %d ptr %p changed %x\n", ctx->def.chan->center_freq, ctx->def.width, ctx, changed); /* This shouldn't really happen because channel switching should use * switch_vif_chanctx(). */ if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) - goto unlock; + return; if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH || changed & IEEE80211_CHANCTX_CHANGE_RADAR || @@ -6662,30 +10098,38 @@ static void ath12k_mac_op_change_chanctx(struct ieee80211_hw *hw, ath12k_mac_update_active_vif_chan(ar, ctx); /* TODO: Recalc radar detection */ - -unlock: - mutex_unlock(&ar->conf_mutex); } static int ath12k_start_vdev_delay(struct ath12k *ar, - struct ath12k_vif *arvif) + struct ath12k_link_vif *arvif) { struct ath12k_base *ab = ar->ab; - struct ieee80211_vif *vif = arvif->vif; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_chanctx_conf *chanctx; + struct ieee80211_bss_conf *link_conf; int ret; if (WARN_ON(arvif->is_started)) return -EBUSY; - ret = ath12k_mac_vdev_start(arvif, &arvif->chanctx); + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ab, "failed to get link conf for vdev %u\n", arvif->vdev_id); + return -EINVAL; + } + + chanctx = wiphy_dereference(ath12k_ar_to_hw(arvif->ar)->wiphy, + link_conf->chanctx_conf); + ret = ath12k_mac_vdev_start(arvif, chanctx); if (ret) { ath12k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", arvif->vdev_id, vif->addr, - arvif->chanctx.def.chan->center_freq, ret); + chanctx->def.chan->center_freq, ret); return ret; } - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ret = ath12k_monitor_vdev_up(ar, arvif->vdev_id); if (ret) { ath12k_warn(ab, "failed put monitor up: %d\n", ret); @@ -6699,6 +10143,391 @@ static int ath12k_start_vdev_delay(struct ath12k *ar, return 0; } +static u8 ath12k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def) +{ + if (chan_def->chan->flags & IEEE80211_CHAN_PSD) { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 4; + case NL80211_CHAN_WIDTH_160: + return 8; + case NL80211_CHAN_WIDTH_320: + return 16; + default: + return 1; + } + } else { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 3; + case NL80211_CHAN_WIDTH_160: + return 4; + case NL80211_CHAN_WIDTH_320: + return 5; + default: + return 1; + } + } +} + +static u16 ath12k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def) +{ + u16 diff_seq; + + /* It is to get the lowest channel number's center frequency of the chan. + * For example, + * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1 + * with center frequency 5955, its diff is 5965 - 5955 = 10. + * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1 + * with center frequency 5955, its diff is 5985 - 5955 = 30. + * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1 + * with center frequency 5955, its diff is 6025 - 5955 = 70. + * bandwidth=320 MHz, center frequency is 6105, lowest channel is 1 + * with center frequency 5955, its diff is 6105 - 5955 = 70. + */ + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_320: + diff_seq = 150; + break; + case NL80211_CHAN_WIDTH_160: + diff_seq = 70; + break; + case NL80211_CHAN_WIDTH_80: + diff_seq = 30; + break; + case NL80211_CHAN_WIDTH_40: + diff_seq = 10; + break; + default: + diff_seq = 0; + } + + return chan_def->center_freq1 - diff_seq; +} + +static u16 ath12k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def, + u16 start_seq, u8 seq) +{ + u16 seg_seq; + + /* It is to get the center frequency of the specific bandwidth. + * start_seq means the lowest channel number's center frequency. + * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz. + * For example, + * lowest channel is 1, its center frequency 5955, + * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30. + * lowest channel is 1, its center frequency 5955, + * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70. + */ + seg_seq = 10 * (BIT(seq) - 1); + return seg_seq + start_seq; +} + +static void ath12k_mac_get_psd_channel(struct ath12k *ar, + u16 step_freq, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + s8 *tx_power) +{ + /* It is to get the center frequency for each 20 MHz. + * For example, if the chan is 160 MHz and center frequency is 6025, + * then it include 8 channels, they are 1/5/9/13/17/21/25/29, + * channel number 1's center frequency is 5955, it is parameter start_freq. + * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels. + * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7, + * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095, + * the gap is 20 for each channel, parameter step_freq means the gap. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = *start_freq + i * step_freq; + *temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +static void ath12k_mac_get_eirp_power(struct ath12k *ar, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + struct cfg80211_chan_def *def, + s8 *tx_power) +{ + /* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/ + * 160 MHz bandwidth, and then plus 10 to the center frequency, + * it is the center frequency of a channel number. + * For example, when configured channel number is 1. + * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975, + * then it is channel number 5. + * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995, + * then it is channel number 9. + * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035, + * then it is channel number 17. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = ath12k_mac_get_seg_freq(def, *start_freq, i); + + /* For the 20 MHz, its center frequency is same with same channel */ + if (i != 0) + *center_freq += 10; + + *temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info; + struct ieee80211_bss_conf *bss_conf = ath12k_mac_get_link_bss_conf(arvif); + struct ieee80211_channel *chan, *temp_chan; + u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction; + bool is_psd_power = false, is_tpe_present = false; + s8 max_tx_power[ATH12K_NUM_PWR_LEVELS], + psd_power, tx_power, eirp_power; + u16 start_freq, center_freq; + + chan = ctx->def.chan; + start_freq = ath12k_mac_get_6ghz_start_frequency(&ctx->def); + pwr_reduction = bss_conf->pwr_reduction; + + if (arvif->reg_tpc_info.num_pwr_levels) { + is_tpe_present = true; + num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels; + } else { + num_pwr_levels = ath12k_mac_get_num_pwr_levels(&ctx->def); + } + + for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) { + /* STA received TPE IE*/ + if (is_tpe_present) { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath12k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + min_t(s8, + psd_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + /* Connecting AP is not psd power */ + } else { + ath12k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + psd_power = temp_chan->psd; + /* convert psd power to EIRP power based + * on channel width + */ + tx_power = + min_t(s8, tx_power, + psd_power + 13 + pwr_lvl_idx * 3); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + /* local power is not PSD power */ + } else { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath12k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + reg_tpc_info->tpe[pwr_lvl_idx]; + /* Connecting AP is not psd power */ + } else { + ath12k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + } + /* STA not received TPE IE */ + } else { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + is_psd_power = true; + ath12k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = psd_power; + } else { + ath12k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = tx_power; + } + } + + if (is_psd_power) { + /* If AP local power constraint is present */ + if (pwr_reduction) + eirp_power = eirp_power - pwr_reduction; + + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + ath12k_dbg(ab, ATH12K_DBG_MAC, + "eirp power : %d firmware report power : %d\n", + eirp_power, ar->max_allowed_tx_power); + /* Firmware reports lower max_allowed_tx_power during vdev + * start response. In case of 6 GHz, firmware is not aware + * of EIRP power unless driver sets EIRP power through WMI + * TPC command. So radio which does not support idle power + * save can set maximum calculated EIRP power directly to + * firmware through TPC command without min comparison with + * vdev start response's max_allowed_tx_power. + */ + if (ar->max_allowed_tx_power && ab->hw_params->idle_ps) + eirp_power = min_t(s8, + eirp_power, + ar->max_allowed_tx_power); + } else { + /* If AP local power constraint is present */ + if (pwr_reduction) + max_tx_power[pwr_lvl_idx] = + max_tx_power[pwr_lvl_idx] - pwr_reduction; + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + if (ar->max_allowed_tx_power && ab->hw_params->idle_ps) + max_tx_power[pwr_lvl_idx] = + min_t(s8, + max_tx_power[pwr_lvl_idx], + ar->max_allowed_tx_power); + } + reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq; + reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power = + max_tx_power[pwr_lvl_idx]; + } + + reg_tpc_info->num_pwr_levels = num_pwr_levels; + reg_tpc_info->is_psd_power = is_psd_power; + reg_tpc_info->eirp_power = eirp_power; + reg_tpc_info->ap_power_type = + ath12k_reg_ap_pwr_convert(bss_conf->power_type); +} + +static void ath12k_mac_parse_tx_pwr_env(struct ath12k *ar, + struct ath12k_link_vif *arvif) +{ + struct ieee80211_bss_conf *bss_conf = ath12k_mac_get_link_bss_conf(arvif); + struct ath12k_reg_tpc_power_info *tpc_info = &arvif->reg_tpc_info; + struct ieee80211_parsed_tpe_eirp *local_non_psd, *reg_non_psd; + struct ieee80211_parsed_tpe_psd *local_psd, *reg_psd; + struct ieee80211_parsed_tpe *tpe = &bss_conf->tpe; + enum wmi_reg_6g_client_type client_type; + struct ath12k_reg_info *reg_info; + struct ath12k_base *ab = ar->ab; + bool psd_valid, non_psd_valid; + int i; + + reg_info = ab->reg_info[ar->pdev_idx]; + client_type = reg_info->client_type; + + local_psd = &tpe->psd_local[client_type]; + reg_psd = &tpe->psd_reg_client[client_type]; + local_non_psd = &tpe->max_local[client_type]; + reg_non_psd = &tpe->max_reg_client[client_type]; + + psd_valid = local_psd->valid | reg_psd->valid; + non_psd_valid = local_non_psd->valid | reg_non_psd->valid; + + if (!psd_valid && !non_psd_valid) { + ath12k_warn(ab, + "no transmit power envelope match client power type %d\n", + client_type); + return; + }; + + if (psd_valid) { + tpc_info->is_psd_power = true; + + tpc_info->num_pwr_levels = max(local_psd->count, + reg_psd->count); + if (tpc_info->num_pwr_levels > ATH12K_NUM_PWR_LEVELS) + tpc_info->num_pwr_levels = ATH12K_NUM_PWR_LEVELS; + + for (i = 0; i < tpc_info->num_pwr_levels; i++) { + tpc_info->tpe[i] = min(local_psd->power[i], + reg_psd->power[i]) / 2; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "TPE PSD power[%d] : %d\n", + i, tpc_info->tpe[i]); + } + } else { + tpc_info->is_psd_power = false; + tpc_info->eirp_power = 0; + + tpc_info->num_pwr_levels = max(local_non_psd->count, + reg_non_psd->count); + if (tpc_info->num_pwr_levels > ATH12K_NUM_PWR_LEVELS) + tpc_info->num_pwr_levels = ATH12K_NUM_PWR_LEVELS; + + for (i = 0; i < tpc_info->num_pwr_levels; i++) { + tpc_info->tpe[i] = min(local_non_psd->power[i], + reg_non_psd->power[i]) / 2; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "non PSD power[%d] : %d\n", + i, tpc_info->tpe[i]); + } + } +} + static int ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -6708,27 +10537,47 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ath12k_base *ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + u8 link_id = link_conf->link_id; + struct ath12k_link_vif *arvif; int ret; - struct ath12k_wmi_peer_create_arg param; - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; + lockdep_assert_wiphy(hw->wiphy); + + /* For multi radio wiphy, the vdev was not created during add_interface + * create now since we have a channel ctx now to assign to a specific ar/fw + */ + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + if (!arvif) { + WARN_ON(1); + return -ENOMEM; + } - mutex_lock(&ar->conf_mutex); + ar = ath12k_mac_assign_vif_to_vdev(hw, arvif, ctx); + if (!ar) { + ath12k_hw_warn(ah, "failed to assign chanctx for vif %pM link id %u link vif is already started", + vif->addr, link_id); + return -EINVAL; + } + + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, - "mac chanctx assign ptr %pK vdev_id %i\n", + "mac chanctx assign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); + if (ath12k_wmi_supports_6ghz_cc_ext(ar) && + ctx->def.chan->band == NL80211_BAND_6GHZ && + ahvif->vdev_type == WMI_VDEV_TYPE_STA) + ath12k_mac_parse_tx_pwr_env(ar, arvif); + arvif->punct_bitmap = ctx->def.punctured; /* for some targets bss peer must be created before vdev_start */ if (ab->hw_params->vdev_start_delay && - arvif->vdev_type != WMI_VDEV_TYPE_AP && - arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && + ahvif->vdev_type != WMI_VDEV_TYPE_AP && + ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR && !ath12k_peer_exist_by_vdev_id(ab, arvif->vdev_id)) { - memcpy(&arvif->chanctx, ctx, sizeof(*ctx)); ret = 0; goto out; } @@ -6738,25 +10587,13 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, goto out; } - if (ab->hw_params->vdev_start_delay && - arvif->vdev_type != WMI_VDEV_TYPE_AP && - arvif->vdev_type != WMI_VDEV_TYPE_MONITOR) { - param.vdev_id = arvif->vdev_id; - param.peer_type = WMI_PEER_TYPE_DEFAULT; - param.peer_addr = ar->mac_addr; - - ret = ath12k_peer_create(ar, arvif, NULL, ¶m); + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath12k_mac_monitor_start(ar); if (ret) { - ath12k_warn(ab, "failed to create peer after vdev start delay: %d", - ret); + ath12k_mac_monitor_vdev_delete(ar); goto out; } - } - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { - ret = ath12k_mac_monitor_start(ar); - if (ret) - goto out; arvif->is_started = true; goto out; } @@ -6769,16 +10606,11 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, goto out; } - if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && ar->monitor_vdev_created) - ath12k_mac_monitor_start(ar); - arvif->is_started = true; /* TODO: Setup ps and cts/rts protection */ out: - mutex_unlock(&ar->conf_mutex); - return ret; } @@ -6788,39 +10620,46 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ath12k_base *ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + u8 link_id = link_conf->link_id; int ret; - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; + lockdep_assert_wiphy(hw->wiphy); + + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - mutex_lock(&ar->conf_mutex); + /* The vif is expected to be attached to an ar's VDEV. + * We leave the vif/vdev in this function as is + * and not delete the vdev symmetric to assign_vif_chanctx() + * the VDEV will be deleted and unassigned either during + * remove_interface() or when there is a change in channel + * that moves the vif to a new ar + */ + if (!arvif || !arvif->is_created) + return; + + ar = arvif->ar; + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, - "mac chanctx unassign ptr %pK vdev_id %i\n", + "mac chanctx unassign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); WARN_ON(!arvif->is_started); - if (ab->hw_params->vdev_start_delay && - arvif->vdev_type == WMI_VDEV_TYPE_MONITOR && - ath12k_peer_find_by_addr(ab, ar->mac_addr)) - ath12k_peer_delete(ar, arvif->vdev_id, ar->mac_addr); - - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ret = ath12k_mac_monitor_stop(ar); - if (ret) { - mutex_unlock(&ar->conf_mutex); + if (ret) return; - } arvif->is_started = false; } - if (arvif->vdev_type != WMI_VDEV_TYPE_STA) { + if (ahvif->vdev_type != WMI_VDEV_TYPE_STA && + ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR) { ath12k_bss_disassoc(ar, arvif); ret = ath12k_mac_vdev_stop(arvif); if (ret) @@ -6829,15 +10668,18 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, } arvif->is_started = false; - if (ab->hw_params->vdev_start_delay && - arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) - ath12k_wmi_vdev_down(ar, arvif->vdev_id); - - if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && - ar->num_started_vdevs == 1 && ar->monitor_vdev_created) - ath12k_mac_monitor_stop(ar); + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE && + ar->state_11d != ATH12K_11D_PREPARING) { + reinit_completion(&ar->completed_11d_scan); + ar->state_11d = ATH12K_11D_PREPARING; + } - mutex_unlock(&ar->conf_mutex); + if (ar->scan.arvif == arvif && ar->scan.state == ATH12K_SCAN_RUNNING) { + ath12k_scan_abort(ar); + ar->scan.arvif = NULL; + } } static int @@ -6846,30 +10688,34 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&ar->conf_mutex); + ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx); + if (!ar) + return -EINVAL; + + /* Switching channels across radio is not allowed */ + if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx)) + return -EINVAL; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac chanctx switch n_vifs %d mode %d\n", n_vifs, mode); ath12k_mac_update_vif_chan(ar, vifs, n_vifs); - mutex_unlock(&ar->conf_mutex); - return 0; } static int ath12k_set_vdev_param_to_all_vifs(struct ath12k *ar, int param, u32 value) { - struct ath12k_vif *arvif; + struct ath12k_link_vif *arvif; int ret = 0; - mutex_lock(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + list_for_each_entry(arvif, &ar->arvifs, list) { ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "setting mac vdev %d param %d value %d\n", param, arvif->vdev_id, value); @@ -6882,7 +10728,7 @@ ath12k_set_vdev_param_to_all_vifs(struct ath12k *ar, int param, u32 value) break; } } - mutex_unlock(&ar->conf_mutex); + return ret; } @@ -6893,11 +10739,23 @@ static int ath12k_mac_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD, ret; + int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD, ret = 0, i; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); - ret = ath12k_set_vdev_param_to_all_vifs(ar, param_id, value); + /* Currently we set the rts threshold value to all the vifs across + * all radios of the single wiphy. + * TODO Once support for vif specific RTS threshold in mac80211 is + * available, ath12k can make use of it. + */ + for_each_ar(ah, ar, i) { + ret = ath12k_set_vdev_param_to_all_vifs(ar, param_id, value); + if (ret) { + ath12k_warn(ar->ab, "failed to set RTS config for all vdevs of pdev %d", + ar->pdev->pdev_id); + break; + } + } return ret; } @@ -6914,37 +10772,83 @@ static int ath12k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) * supported. This effectively prevents mac80211 from doing frame * fragmentation in software. */ + + lockdep_assert_wiphy(hw->wiphy); + return -EOPNOTSUPP; } -static void ath12k_mac_flush(struct ath12k *ar) +static int ath12k_mac_flush(struct ath12k *ar) { long time_left; + int ret = 0; time_left = wait_event_timeout(ar->dp.tx_empty_waitq, (atomic_read(&ar->dp.num_tx_pending) == 0), ATH12K_FLUSH_TIMEOUT); - if (time_left == 0) - ath12k_warn(ar->ab, "failed to flush transmit queue %ld\n", time_left); + if (time_left == 0) { + ath12k_warn(ar->ab, + "failed to flush transmit queue, data pkts pending %d\n", + atomic_read(&ar->dp.num_tx_pending)); + ret = -ETIMEDOUT; + } time_left = wait_event_timeout(ar->txmgmt_empty_waitq, (atomic_read(&ar->num_pending_mgmt_tx) == 0), ATH12K_FLUSH_TIMEOUT); - if (time_left == 0) - ath12k_warn(ar->ab, "failed to flush mgmt transmit queue %ld\n", - time_left); + if (time_left == 0) { + ath12k_warn(ar->ab, + "failed to flush mgmt transmit queue, mgmt pkts pending %d\n", + atomic_read(&ar->num_pending_mgmt_tx)); + ret = -ETIMEDOUT; + } + + return ret; +} + +int ath12k_mac_wait_tx_complete(struct ath12k *ar) +{ + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + ath12k_mac_drain_tx(ar); + return ath12k_mac_flush(ar); } static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar = ath12k_ah_to_ar(ah); + struct ath12k_link_vif *arvif; + struct ath12k_vif *ahvif; + unsigned long links; + struct ath12k *ar; + u8 link_id; + int i; + + lockdep_assert_wiphy(hw->wiphy); if (drop) return; - ath12k_mac_flush(ar); + /* vif can be NULL when flush() is considered for hw */ + if (!vif) { + for_each_ar(ah, ar, i) + ath12k_mac_flush(ar); + return; + } + + for_each_ar(ah, ar, i) + wiphy_work_flush(hw->wiphy, &ar->wmi_mgmt_tx_work); + + ahvif = ath12k_vif_to_ahvif(vif); + links = ahvif->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (!(arvif && arvif->ar)) + continue; + + ath12k_mac_flush(arvif->ar); + } } static int @@ -7062,14 +10966,14 @@ ath12k_mac_get_single_legacy_rate(struct ath12k *ar, return 0; } -static int ath12k_mac_set_fixed_rate_params(struct ath12k_vif *arvif, +static int ath12k_mac_set_fixed_rate_params(struct ath12k_link_vif *arvif, u32 rate, u8 nss, u8 sgi, u8 ldpc) { struct ath12k *ar = arvif->ar; u32 vdev_param; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac set fixed rate params vdev %i rate 0x%02x nss %u sgi %u\n", arvif->vdev_id, rate, nss, sgi); @@ -7141,32 +11045,47 @@ ath12k_mac_vht_mcs_range_present(struct ath12k *ar, static void ath12k_mac_set_bitrate_mask_iter(void *data, struct ieee80211_sta *sta) { - struct ath12k_vif *arvif = data; - struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); + struct ath12k_link_vif *arvif = data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_link_sta *arsta; struct ath12k *ar = arvif->ar; + arsta = rcu_dereference(ahsta->link[arvif->link_id]); + if (!arsta || arsta->arvif != arvif) + return; + spin_lock_bh(&ar->data_lock); arsta->changed |= IEEE80211_RC_SUPP_RATES_CHANGED; spin_unlock_bh(&ar->data_lock); - ieee80211_queue_work(ath12k_ar_to_hw(ar), &arsta->update_wk); + wiphy_work_queue(ath12k_ar_to_hw(ar)->wiphy, &arsta->update_wk); } static void ath12k_mac_disable_peer_fixed_rate(void *data, struct ieee80211_sta *sta) { - struct ath12k_vif *arvif = data; + struct ath12k_link_vif *arvif = data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_link_sta *arsta; struct ath12k *ar = arvif->ar; int ret; - ret = ath12k_wmi_set_peer_param(ar, sta->addr, + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + ahsta->link[arvif->link_id]); + + if (!arsta || arsta->arvif != arvif) + return; + + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id, WMI_PEER_PARAM_FIXED_RATE, WMI_FIXED_RATE_NONE); if (ret) ath12k_warn(ar->ab, "failed to disable peer fixed rate for STA %pM ret %d\n", - sta->addr, ret); + arsta->addr, ret); } static int @@ -7174,9 +11093,10 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; struct cfg80211_chan_def def; - struct ath12k *ar = arvif->ar; + struct ath12k *ar; enum nl80211_band band; const u8 *ht_mcs_mask; const u16 *vht_mcs_mask; @@ -7188,8 +11108,15 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, int ret; int num_rates; - if (ath12k_mac_vif_chan(vif, &def)) - return -EPERM; + lockdep_assert_wiphy(hw->wiphy); + + arvif = &ahvif->deflink; + + ar = arvif->ar; + if (ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)) { + ret = -EPERM; + goto out; + } band = def.chan->band; ht_mcs_mask = mask->control[band].ht_mcs; @@ -7218,9 +11145,9 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, arvif->vdev_id, ret); goto out; } - ieee80211_iterate_stations_atomic(hw, - ath12k_mac_disable_peer_fixed_rate, - arvif); + ieee80211_iterate_stations_mtx(hw, + ath12k_mac_disable_peer_fixed_rate, + arvif); } else if (ath12k_mac_bitrate_mask_get_single_nss(ar, band, mask, &single_nss)) { rate = WMI_FIXED_RATE_NONE; @@ -7261,34 +11188,25 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, */ ath12k_warn(ar->ab, "Setting more than one MCS Value in bitrate mask not supported\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } - ieee80211_iterate_stations_atomic(hw, - ath12k_mac_disable_peer_fixed_rate, - arvif); - - mutex_lock(&ar->conf_mutex); + ieee80211_iterate_stations_mtx(hw, + ath12k_mac_disable_peer_fixed_rate, + arvif); arvif->bitrate_mask = *mask; - ieee80211_iterate_stations_atomic(hw, - ath12k_mac_set_bitrate_mask_iter, - arvif); - - mutex_unlock(&ar->conf_mutex); + ieee80211_iterate_stations_mtx(hw, + ath12k_mac_set_bitrate_mask_iter, + arvif); } - mutex_lock(&ar->conf_mutex); - ret = ath12k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); if (ret) { ath12k_warn(ar->ab, "failed to set fixed rate params on vdev %i: %d\n", arvif->vdev_id, ret); } - mutex_unlock(&ar->conf_mutex); - out: return ret; } @@ -7300,27 +11218,43 @@ ath12k_mac_op_reconfig_complete(struct ieee80211_hw *hw, struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ath12k_base *ab; - struct ath12k_vif *arvif; - int recovery_count; + struct ath12k_vif *ahvif; + struct ath12k_link_vif *arvif; + int recovery_count, i; + + lockdep_assert_wiphy(hw->wiphy); if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return; - ar = ath12k_ah_to_ar(ah); - ab = ar->ab; + guard(mutex)(&ah->hw_mutex); + + if (ah->state != ATH12K_HW_STATE_RESTARTED) + return; - mutex_lock(&ar->conf_mutex); + ah->state = ATH12K_HW_STATE_ON; + ieee80211_wake_queues(hw); + + for_each_ar(ah, ar, i) { + ab = ar->ab; - if (ar->state == ATH12K_STATE_RESTARTED) { ath12k_warn(ar->ab, "pdev %d successfully recovered\n", ar->pdev->pdev_id); - ar->state = ATH12K_STATE_ON; - ieee80211_wake_queues(hw); + + if (ar->ab->hw_params->current_cc_support && + ar->alpha2[0] != 0 && ar->alpha2[1] != 0) { + struct wmi_set_current_country_arg arg = {}; + + memcpy(&arg.alpha2, ar->alpha2, 2); + ath12k_wmi_send_set_current_country_cmd(ar, &arg); + } if (ab->is_reset) { recovery_count = atomic_inc_return(&ab->recovery_count); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "recovery count %d\n", recovery_count); + /* When there are multiple radios in an SOC, * the recovery has to be done for each radio */ @@ -7334,27 +11268,28 @@ ath12k_mac_op_reconfig_complete(struct ieee80211_hw *hw, } list_for_each_entry(arvif, &ar->arvifs, list) { + ahvif = arvif->ahvif; ath12k_dbg(ab, ATH12K_DBG_BOOT, "reconfig cipher %d up %d vdev type %d\n", - arvif->key_cipher, + ahvif->key_cipher, arvif->is_up, - arvif->vdev_type); + ahvif->vdev_type); + /* After trigger disconnect, then upper layer will * trigger connect again, then the PN number of * upper layer will be reset to keep up with AP * side, hence PN number mismatch will not happen. */ if (arvif->is_up && - arvif->vdev_type == WMI_VDEV_TYPE_STA && - arvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { - ieee80211_hw_restart_disconnect(arvif->vif); + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { + ieee80211_hw_restart_disconnect(ahvif->vif); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "restart disconnect\n"); } } } - - mutex_unlock(&ar->conf_mutex); } static void @@ -7364,7 +11299,7 @@ ath12k_mac_update_bss_chan_survey(struct ath12k *ar, int ret; enum wmi_bss_chan_info_req_type type = WMI_BSS_SURVEY_REQ_TYPE_READ; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (!test_bit(WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64, ar->ab->wmi_ab.svc_map) || ar->rx_channel != channel) @@ -7392,21 +11327,15 @@ ath12k_mac_update_bss_chan_survey(struct ath12k *ar, static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; struct ieee80211_supported_band *sband; struct survey_info *ar_survey; - int ret = 0; + + lockdep_assert_wiphy(hw->wiphy); if (idx >= ATH12K_NUM_CHANS) return -ENOENT; - ar = ath12k_ah_to_ar(ah); - - ar_survey = &ar->survey[idx]; - - mutex_lock(&ar->conf_mutex); - sband = hw->wiphy->bands[NL80211_BAND_2GHZ]; if (sband && idx >= sband->n_channels) { idx -= sband->n_channels; @@ -7415,12 +11344,28 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, if (!sband) sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } - if (!sband || idx >= sband->n_channels) { - ret = -ENOENT; - goto exit; + if (!sband) + sband = hw->wiphy->bands[NL80211_BAND_6GHZ]; + + if (!sband || idx >= sband->n_channels) + return -ENOENT; + + ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[idx]); + if (!ar) { + if (sband->channels[idx].flags & IEEE80211_CHAN_DISABLED) { + memset(survey, 0, sizeof(*survey)); + return 0; + } + return -ENOENT; } + ar_survey = &ar->survey[idx]; + ath12k_mac_update_bss_chan_survey(ar, &sband->channels[idx]); spin_lock_bh(&ar->data_lock); @@ -7432,10 +11377,7 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, if (ar->rx_channel == survey->channel) survey->filled |= SURVEY_INFO_IN_USE; -exit: - mutex_unlock(&ar->conf_mutex); - - return ret; + return 0; } static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, @@ -7443,7 +11385,22 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct station_info *sinfo) { - struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_fw_stats_req_params params = {}; + struct ath12k_link_sta *arsta; + struct ath12k *ar; + s8 signal; + bool db2dbm; + + lockdep_assert_wiphy(hw->wiphy); + + arsta = &ahsta->deflink; + ar = ath12k_get_ar_by_vif(hw, vif, arsta->link_id); + if (!ar) + return; + + db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ar->ab->wmi_ab.svc_map); sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); @@ -7451,25 +11408,46 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_duration = arsta->tx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION); - if (!arsta->txrate.legacy && !arsta->txrate.nss) - return; - - if (arsta->txrate.legacy) { - sinfo->txrate.legacy = arsta->txrate.legacy; - } else { - sinfo->txrate.mcs = arsta->txrate.mcs; - sinfo->txrate.nss = arsta->txrate.nss; - sinfo->txrate.bw = arsta->txrate.bw; - sinfo->txrate.he_gi = arsta->txrate.he_gi; - sinfo->txrate.he_dcm = arsta->txrate.he_dcm; - sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + if (arsta->txrate.legacy || arsta->txrate.nss) { + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + sinfo->txrate.he_gi = arsta->txrate.he_gi; + sinfo->txrate.he_dcm = arsta->txrate.he_dcm; + sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + sinfo->txrate.eht_gi = arsta->txrate.eht_gi; + sinfo->txrate.eht_ru_alloc = arsta->txrate.eht_ru_alloc; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } - sinfo->txrate.flags = arsta->txrate.flags; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); /* TODO: Use real NF instead of default one. */ - sinfo->signal = arsta->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + signal = arsta->rssi_comb; + + params.pdev_id = ar->pdev->pdev_id; + params.vdev_id = 0; + params.stats_id = WMI_REQUEST_VDEV_STAT; + + if (!signal && + ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA && + !(ath12k_mac_get_fw_stats(ar, ¶ms))) + signal = arsta->rssi_beacon; + + if (signal) { + sinfo->signal = db2dbm ? signal : signal + ATH12K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } + + sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi); + + if (!db2dbm) + sinfo->signal_avg += ATH12K_DEFAULT_NOISE_FLOOR; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); } static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, @@ -7478,9 +11456,9 @@ static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; - ar = ath12k_ah_to_ar(ah); + ar = ath12k_ah_to_ar(ah, 0); - mutex_lock(&ar->conf_mutex); + lockdep_assert_wiphy(hw->wiphy); spin_lock_bh(&ar->data_lock); ar->scan.roc_notify = false; @@ -7488,9 +11466,8 @@ static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, ath12k_scan_abort(ar); - mutex_unlock(&ar->conf_mutex); - cancel_delayed_work_sync(&ar->scan.timeout); + wiphy_work_cancel(hw->wiphy, &ar->scan.vdev_clean_wk); return 0; } @@ -7501,16 +11478,63 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, int duration, enum ieee80211_roc_type type) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k_wmi_scan_req_arg arg; + struct ath12k_link_vif *arvif; struct ath12k *ar; u32 scan_time_msec; + bool create = true; + u8 link_id; int ret; - ar = ath12k_ah_to_ar(ah); + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_mac_select_scan_device(hw, vif, chan->center_freq); + if (!ar) + return -EINVAL; + + /* check if any of the links of ML VIF is already started on + * radio(ar) corresponding to given scan frequency and use it, + * if not use deflink(link 0) for scan purpose. + */ + + link_id = ath12k_mac_find_link_id_by_ar(ahvif, ar); + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + /* If the vif is already assigned to a specific vdev of an ar, + * check whether its already started, vdev which is started + * are not allowed to switch to a new radio. + * If the vdev is not started, but was earlier created on a + * different ar, delete that vdev and create a new one. We don't + * delete at the scan stop as an optimization to avoid redundant + * delete-create vdev's for the same ar, in case the request is + * always on the same band for the vif + */ + if (arvif->is_created) { + if (WARN_ON(!arvif->ar)) + return -EINVAL; + + if (ar != arvif->ar && arvif->is_started) + return -EBUSY; + + if (ar != arvif->ar) { + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } else { + create = false; + } + } + + if (create) { + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + + ret = ath12k_mac_vdev_create(ar, arvif); + if (ret) { + ath12k_warn(ar->ab, "unable to create scan vdev for roc: %d\n", + ret); + return ret; + } + } - mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); switch (ar->scan.state) { @@ -7520,7 +11544,7 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, reinit_completion(&ar->scan.on_channel); ar->scan.state = ATH12K_SCAN_STARTING; ar->scan.is_roc = true; - ar->scan.vdev_id = arvif->vdev_id; + ar->scan.arvif = arvif; ar->scan.roc_freq = chan->center_freq; ar->scan.roc_notify = true; ret = 0; @@ -7535,37 +11559,41 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); if (ret) - goto exit; + return ret; scan_time_msec = hw->wiphy->max_remain_on_channel_duration * 2; - memset(&arg, 0, sizeof(arg)); - ath12k_wmi_start_scan_init(ar, &arg); - arg.num_chan = 1; - arg.chan_list = kcalloc(arg.num_chan, sizeof(*arg.chan_list), - GFP_KERNEL); - if (!arg.chan_list) { - ret = -ENOMEM; - goto exit; - } + struct ath12k_wmi_scan_req_arg *arg __free(kfree) = + kzalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) + return -ENOMEM; - arg.vdev_id = arvif->vdev_id; - arg.scan_id = ATH12K_SCAN_ID; - arg.chan_list[0] = chan->center_freq; - arg.dwell_time_active = scan_time_msec; - arg.dwell_time_passive = scan_time_msec; - arg.max_scan_time = scan_time_msec; - arg.scan_f_passive = 1; - arg.burst_duration = duration; - - ret = ath12k_start_scan(ar, &arg); + ath12k_wmi_start_scan_init(ar, arg); + arg->num_chan = 1; + + u32 *chan_list __free(kfree) = kcalloc(arg->num_chan, sizeof(*chan_list), + GFP_KERNEL); + if (!chan_list) + return -ENOMEM; + + arg->chan_list = chan_list; + arg->vdev_id = arvif->vdev_id; + arg->scan_id = ATH12K_SCAN_ID; + arg->chan_list[0] = chan->center_freq; + arg->dwell_time_active = scan_time_msec; + arg->dwell_time_passive = scan_time_msec; + arg->max_scan_time = scan_time_msec; + arg->scan_f_passive = 1; + arg->burst_duration = duration; + + ret = ath12k_start_scan(ar, arg); if (ret) { ath12k_warn(ar->ab, "failed to start roc scan: %d\n", ret); spin_lock_bh(&ar->data_lock); ar->scan.state = ATH12K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); - goto free_chan_list; + return ret; } ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ); @@ -7574,21 +11602,49 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, ret = ath12k_scan_stop(ar); if (ret) ath12k_warn(ar->ab, "failed to stop scan: %d\n", ret); - ret = -ETIMEDOUT; - goto free_chan_list; + return -ETIMEDOUT; } ieee80211_queue_delayed_work(hw, &ar->scan.timeout, msecs_to_jiffies(duration)); - ret = 0; + return 0; +} -free_chan_list: - kfree(arg.chan_list); -exit: - mutex_unlock(&ar->conf_mutex); +static void ath12k_mac_op_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_rekey_data *rekey_data; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + struct ath12k_link_vif *arvif; - return ret; + lockdep_assert_wiphy(hw->wiphy); + + arvif = &ahvif->deflink; + rekey_data = &arvif->rekey_data; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac set rekey data vdev %d\n", + arvif->vdev_id); + + memcpy(rekey_data->kck, data->kck, NL80211_KCK_LEN); + memcpy(rekey_data->kek, data->kek, NL80211_KEK_LEN); + + /* The supplicant works on big-endian, the firmware expects it on + * little endian. + */ + rekey_data->replay_ctr = get_unaligned_be64(data->replay_ctr); + + arvif->rekey_data.enable_offload = true; + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "kck", NULL, + rekey_data->kck, NL80211_KCK_LEN); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "kek", NULL, + rekey_data->kck, NL80211_KEK_LEN); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "replay ctr", NULL, + &rekey_data->replay_ctr, sizeof(rekey_data->replay_ctr)); } static const struct ieee80211_ops ath12k_ops = { @@ -7601,14 +11657,17 @@ static const struct ieee80211_ops ath12k_ops = { .remove_interface = ath12k_mac_op_remove_interface, .update_vif_offload = ath12k_mac_op_update_vif_offload, .config = ath12k_mac_op_config, - .bss_info_changed = ath12k_mac_op_bss_info_changed, + .link_info_changed = ath12k_mac_op_link_info_changed, + .vif_cfg_changed = ath12k_mac_op_vif_cfg_changed, + .change_vif_links = ath12k_mac_op_change_vif_links, .configure_filter = ath12k_mac_op_configure_filter, .hw_scan = ath12k_mac_op_hw_scan, .cancel_hw_scan = ath12k_mac_op_cancel_hw_scan, .set_key = ath12k_mac_op_set_key, + .set_rekey_data = ath12k_mac_op_set_rekey_data, .sta_state = ath12k_mac_op_sta_state, .sta_set_txpwr = ath12k_mac_op_sta_set_txpwr, - .sta_rc_update = ath12k_mac_op_sta_rc_update, + .link_sta_rc_update = ath12k_mac_op_link_sta_rc_update, .conf_tx = ath12k_mac_op_conf_tx, .set_antenna = ath12k_mac_op_set_antenna, .get_antenna = ath12k_mac_op_get_antenna, @@ -7619,6 +11678,7 @@ static const struct ieee80211_ops ath12k_ops = { .assign_vif_chanctx = ath12k_mac_op_assign_vif_chanctx, .unassign_vif_chanctx = ath12k_mac_op_unassign_vif_chanctx, .switch_vif_chanctx = ath12k_mac_op_switch_vif_chanctx, + .get_txpower = ath12k_mac_op_get_txpower, .set_rts_threshold = ath12k_mac_op_set_rts_threshold, .set_frag_threshold = ath12k_mac_op_set_frag_threshold, .set_bitrate_mask = ath12k_mac_op_set_bitrate_mask, @@ -7627,8 +11687,44 @@ static const struct ieee80211_ops ath12k_ops = { .sta_statistics = ath12k_mac_op_sta_statistics, .remain_on_channel = ath12k_mac_op_remain_on_channel, .cancel_remain_on_channel = ath12k_mac_op_cancel_remain_on_channel, + .change_sta_links = ath12k_mac_op_change_sta_links, + .can_activate_links = ath12k_mac_op_can_activate_links, +#ifdef CONFIG_PM + .suspend = ath12k_wow_op_suspend, + .resume = ath12k_wow_op_resume, + .set_wakeup = ath12k_wow_op_set_wakeup, +#endif +#ifdef CONFIG_ATH12K_DEBUGFS + .vif_add_debugfs = ath12k_debugfs_op_vif_add, +#endif + CFG80211_TESTMODE_CMD(ath12k_tm_cmd) +#ifdef CONFIG_ATH12K_DEBUGFS + .link_sta_add_debugfs = ath12k_debugfs_link_sta_op_add, +#endif }; +void ath12k_mac_update_freq_range(struct ath12k *ar, + u32 freq_low, u32 freq_high) +{ + if (!(freq_low && freq_high)) + return; + + if (ar->freq_range.start_freq || ar->freq_range.end_freq) { + ar->freq_range.start_freq = min(ar->freq_range.start_freq, + MHZ_TO_KHZ(freq_low)); + ar->freq_range.end_freq = max(ar->freq_range.end_freq, + MHZ_TO_KHZ(freq_high)); + } else { + ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low); + ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "mac pdev %u freq limit updated. New range %u->%u MHz\n", + ar->pdev->pdev_id, KHZ_TO_MHZ(ar->freq_range.start_freq), + KHZ_TO_MHZ(ar->freq_range.end_freq)); +} + static void ath12k_mac_update_ch_list(struct ath12k *ar, struct ieee80211_supported_band *band, u32 freq_low, u32 freq_high) @@ -7650,10 +11746,10 @@ static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band) struct ath12k_pdev *pdev = ar->pdev; struct ath12k_pdev_cap *pdev_cap = &pdev->cap; - if (band == WMI_HOST_WLAN_2G_CAP) + if (band == WMI_HOST_WLAN_2GHZ_CAP) return pdev_cap->band[NL80211_BAND_2GHZ].phy_id; - if (band == WMI_HOST_WLAN_5G_CAP) + if (band == WMI_HOST_WLAN_5GHZ_CAP) return pdev_cap->band[NL80211_BAND_5GHZ].phy_id; ath12k_warn(ar->ab, "unsupported phy cap:%d\n", band); @@ -7667,17 +11763,19 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, { struct ieee80211_supported_band *band; struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; + struct ath12k_base *ab = ar->ab; + u32 phy_id, freq_low, freq_high; + struct ath12k_hw *ah = ar->ah; void *channels; - u32 phy_id; BUILD_BUG_ON((ARRAY_SIZE(ath12k_2ghz_channels) + ARRAY_SIZE(ath12k_5ghz_channels) + ARRAY_SIZE(ath12k_6ghz_channels)) != ATH12K_NUM_CHANS); - reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx]; + reg_cap = &ab->hal_reg_cap[ar->pdev_idx]; - if (supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { channels = kmemdup(ath12k_2ghz_channels, sizeof(ath12k_2ghz_channels), GFP_KERNEL); @@ -7692,17 +11790,25 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->bitrates = ath12k_g_rates; bands[NL80211_BAND_2GHZ] = band; - if (ar->ab->hw_params->single_pdev_only) { - phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2G_CAP); - reg_cap = &ar->ab->hal_reg_cap[phy_id]; + if (ab->hw_params->single_pdev_only) { + phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2GHZ_CAP); + reg_cap = &ab->hal_reg_cap[phy_id]; } + + freq_low = max(reg_cap->low_2ghz_chan, + ab->reg_freq_2ghz.start_freq); + freq_high = min(reg_cap->high_2ghz_chan, + ab->reg_freq_2ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_2ghz_chan, reg_cap->high_2ghz_chan); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); } - if (supported_bands & WMI_HOST_WLAN_5G_CAP) { - if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6G_FREQ) { + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { + if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6GHZ_FREQ) { channels = kmemdup(ath12k_6ghz_channels, sizeof(ath12k_6ghz_channels), GFP_KERNEL); if (!channels) { @@ -7718,12 +11824,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->n_bitrates = ath12k_a_rates_size; band->bitrates = ath12k_a_rates; bands[NL80211_BAND_6GHZ] = band; + + freq_low = max(reg_cap->low_5ghz_chan, + ab->reg_freq_6ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, + ab->reg_freq_6ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + ah->use_6ghz_regd = true; } - if (reg_cap->low_5ghz_chan < ATH12K_MIN_6G_FREQ) { + if (reg_cap->low_5ghz_chan < ATH12K_MIN_6GHZ_FREQ) { channels = kmemdup(ath12k_5ghz_channels, sizeof(ath12k_5ghz_channels), GFP_KERNEL); @@ -7741,14 +11856,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->bitrates = ath12k_a_rates; bands[NL80211_BAND_5GHZ] = band; - if (ar->ab->hw_params->single_pdev_only) { - phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP); - reg_cap = &ar->ab->hal_reg_cap[phy_id]; + if (ab->hw_params->single_pdev_only) { + phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5GHZ_CAP); + reg_cap = &ab->hal_reg_cap[phy_id]; } + freq_low = max(reg_cap->low_5ghz_chan, + ab->reg_freq_5ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, + ab->reg_freq_5ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); } } @@ -7757,10 +11879,12 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, static u16 ath12k_mac_get_ifmodes(struct ath12k_hw *ah) { - struct ath12k *ar = ath12k_ah_to_ar(ah); + struct ath12k *ar; + int i; u16 interface_modes = U16_MAX; - interface_modes &= ar->ab->hw_params->interface_modes; + for_each_ar(ah, ar, i) + interface_modes &= ar->ab->hw_params->interface_modes; return interface_modes == U16_MAX ? 0 : interface_modes; } @@ -7768,36 +11892,43 @@ static u16 ath12k_mac_get_ifmodes(struct ath12k_hw *ah) static bool ath12k_mac_is_iface_mode_enable(struct ath12k_hw *ah, enum nl80211_iftype type) { - struct ath12k *ar = ath12k_ah_to_ar(ah); - u16 interface_modes, mode; - bool is_enable = true; + struct ath12k *ar; + int i; + u16 interface_modes, mode = 0; + bool is_enable = false; - mode = BIT(type); + if (type == NL80211_IFTYPE_MESH_POINT) { + if (IS_ENABLED(CONFIG_MAC80211_MESH)) + mode = BIT(type); + } else { + mode = BIT(type); + } - interface_modes = ar->ab->hw_params->interface_modes; - if (!(interface_modes & mode)) - is_enable = false; + for_each_ar(ah, ar, i) { + interface_modes = ar->ab->hw_params->interface_modes; + if (interface_modes & mode) { + is_enable = true; + break; + } + } return is_enable; } -static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah) +static int +ath12k_mac_setup_radio_iface_comb(struct ath12k *ar, + struct ieee80211_iface_combination *comb) { - struct wiphy *wiphy = ah->hw->wiphy; - struct ieee80211_iface_combination *combinations; + u16 interface_modes = ar->ab->hw_params->interface_modes; struct ieee80211_iface_limit *limits; int n_limits, max_interfaces; bool ap, mesh, p2p; - ap = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_AP); - p2p = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_P2P_DEVICE); + ap = interface_modes & BIT(NL80211_IFTYPE_AP); + p2p = interface_modes & BIT(NL80211_IFTYPE_P2P_DEVICE); mesh = IS_ENABLED(CONFIG_MAC80211_MESH) && - ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_MESH_POINT); - - combinations = kzalloc(sizeof(*combinations), GFP_KERNEL); - if (!combinations) - return -ENOMEM; + (interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)); if ((ap || mesh) && !p2p) { n_limits = 2; @@ -7814,10 +11945,8 @@ static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah) } limits = kcalloc(n_limits, sizeof(*limits), GFP_KERNEL); - if (!limits) { - kfree(combinations); + if (!limits) return -ENOMEM; - } limits[0].max = 1; limits[0].types |= BIT(NL80211_IFTYPE_STATION); @@ -7833,46 +11962,210 @@ static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah) if (p2p) { limits[1].types |= BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); + BIT(NL80211_IFTYPE_P2P_GO); limits[2].max = 1; limits[2].types |= BIT(NL80211_IFTYPE_P2P_DEVICE); } - combinations[0].limits = limits; - combinations[0].n_limits = n_limits; - combinations[0].max_interfaces = max_interfaces; - combinations[0].num_different_channels = 1; - combinations[0].beacon_int_infra_match = true; - combinations[0].beacon_int_min_gcd = 100; - combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + comb[0].limits = limits; + comb[0].n_limits = n_limits; + comb[0].max_interfaces = max_interfaces; + comb[0].beacon_int_infra_match = true; + comb[0].beacon_int_min_gcd = 100; + + if (ar->ab->hw_params->single_pdev_only) { + comb[0].num_different_channels = 2; + } else { + comb[0].num_different_channels = 1; + comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80); + } + + return 0; +} + +static int +ath12k_mac_setup_global_iface_comb(struct ath12k_hw *ah, + struct wiphy_radio *radio, + u8 n_radio, + struct ieee80211_iface_combination *comb) +{ + const struct ieee80211_iface_combination *iter_comb; + struct ieee80211_iface_limit *limits; + int i, j, n_limits; + bool ap, mesh, p2p; + + if (!n_radio) + return 0; + + ap = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_AP); + p2p = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_P2P_DEVICE); + mesh = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_MESH_POINT); + + if ((ap || mesh) && !p2p) + n_limits = 2; + else if (p2p) + n_limits = 3; + else + n_limits = 1; + limits = kcalloc(n_limits, sizeof(*limits), GFP_KERNEL); + if (!limits) + return -ENOMEM; + + for (i = 0; i < n_radio; i++) { + iter_comb = radio[i].iface_combinations; + for (j = 0; j < iter_comb->n_limits && j < n_limits; j++) { + limits[j].types |= iter_comb->limits[j].types; + limits[j].max += iter_comb->limits[j].max; + } + + comb->max_interfaces += iter_comb->max_interfaces; + comb->num_different_channels += iter_comb->num_different_channels; + comb->radar_detect_widths |= iter_comb->radar_detect_widths; + } + + comb->limits = limits; + comb->n_limits = n_limits; + comb->beacon_int_infra_match = true; + comb->beacon_int_min_gcd = 100; + + return 0; +} + +static +void ath12k_mac_cleanup_iface_comb(const struct ieee80211_iface_combination *iface_comb) +{ + kfree(iface_comb[0].limits); + kfree(iface_comb); +} + +static void ath12k_mac_cleanup_iface_combinations(struct ath12k_hw *ah) +{ + struct wiphy *wiphy = ah->hw->wiphy; + const struct wiphy_radio *radio; + int i; + + if (wiphy->n_radio > 0) { + radio = wiphy->radio; + for (i = 0; i < wiphy->n_radio; i++) + ath12k_mac_cleanup_iface_comb(radio[i].iface_combinations); + + kfree(wiphy->radio); + } + + ath12k_mac_cleanup_iface_comb(wiphy->iface_combinations); +} + +static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah) +{ + struct ieee80211_iface_combination *combinations, *comb; + struct wiphy *wiphy = ah->hw->wiphy; + struct wiphy_radio *radio; + struct ath12k *ar; + int i, ret; + + combinations = kzalloc(sizeof(*combinations), GFP_KERNEL); + if (!combinations) + return -ENOMEM; + + if (ah->num_radio == 1) { + ret = ath12k_mac_setup_radio_iface_comb(&ah->radio[0], + combinations); + if (ret) { + ath12k_hw_warn(ah, "failed to setup radio interface combinations for one radio: %d", + ret); + goto err_free_combinations; + } + + goto out; + } + + /* there are multiple radios */ + + radio = kcalloc(ah->num_radio, sizeof(*radio), GFP_KERNEL); + if (!radio) { + ret = -ENOMEM; + goto err_free_combinations; + } + + for_each_ar(ah, ar, i) { + comb = kzalloc(sizeof(*comb), GFP_KERNEL); + if (!comb) { + ret = -ENOMEM; + goto err_free_radios; + } + + ret = ath12k_mac_setup_radio_iface_comb(ar, comb); + if (ret) { + ath12k_hw_warn(ah, "failed to setup radio interface combinations for radio %d: %d", + i, ret); + kfree(comb); + goto err_free_radios; + } + + radio[i].freq_range = &ar->freq_range; + radio[i].n_freq_range = 1; + + radio[i].iface_combinations = comb; + radio[i].n_iface_combinations = 1; + } + + ret = ath12k_mac_setup_global_iface_comb(ah, radio, ah->num_radio, combinations); + if (ret) { + ath12k_hw_warn(ah, "failed to setup global interface combinations: %d", + ret); + goto err_free_all_radios; + } + + wiphy->radio = radio; + wiphy->n_radio = ah->num_radio; + +out: wiphy->iface_combinations = combinations; wiphy->n_iface_combinations = 1; return 0; + +err_free_all_radios: + i = ah->num_radio; + +err_free_radios: + while (i--) + ath12k_mac_cleanup_iface_comb(radio[i].iface_combinations); + + kfree(radio); + +err_free_combinations: + kfree(combinations); + + return ret; } static const u8 ath12k_if_types_ext_capa[] = { [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, }; static const u8 ath12k_if_types_ext_capa_sta[] = { [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT, }; static const u8 ath12k_if_types_ext_capa_ap[] = { [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, [9] = WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT, + [10] = WLAN_EXT_CAPA11_EMA_SUPPORT, }; -static const struct wiphy_iftype_ext_capab ath12k_iftypes_ext_capa[] = { +static struct wiphy_iftype_ext_capab ath12k_iftypes_ext_capa[] = { { .extended_capabilities = ath12k_if_types_ext_capa, .extended_capabilities_mask = ath12k_if_types_ext_capa, @@ -7889,6 +12182,8 @@ static const struct wiphy_iftype_ext_capab ath12k_iftypes_ext_capa[] = { .extended_capabilities_mask = ath12k_if_types_ext_capa_ap, .extended_capabilities_len = sizeof(ath12k_if_types_ext_capa_ap), + .eml_capabilities = 0, + .mld_capa_and_ops = 0, }, }; @@ -7905,17 +12200,21 @@ static void ath12k_mac_cleanup_unregister(struct ath12k *ar) static void ath12k_mac_hw_unregister(struct ath12k_hw *ah) { struct ieee80211_hw *hw = ah->hw; - struct wiphy *wiphy = hw->wiphy; - struct ath12k *ar = ath12k_ah_to_ar(ah); + struct ath12k *ar; + int i; - cancel_work_sync(&ar->regd_update_work); + for_each_ar(ah, ar, i) { + cancel_work_sync(&ar->regd_update_work); + ath12k_debugfs_unregister(ar); + ath12k_fw_stats_reset(ar); + } ieee80211_unregister_hw(hw); - ath12k_mac_cleanup_unregister(ar); + for_each_ar(ah, ar, i) + ath12k_mac_cleanup_unregister(ar); - kfree(wiphy->iface_combinations[0].limits); - kfree(wiphy->iface_combinations); + ath12k_mac_cleanup_iface_combinations(ah); SET_IEEE80211_DEV(hw, NULL); } @@ -7952,7 +12251,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) { struct ieee80211_hw *hw = ah->hw; struct wiphy *wiphy = hw->wiphy; - struct ath12k *ar = ath12k_ah_to_ar(ah); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); struct ath12k_base *ab = ar->ab; struct ath12k_pdev *pdev; struct ath12k_pdev_cap *cap; @@ -7967,39 +12266,77 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) WLAN_CIPHER_SUITE_GCMP_256, WLAN_CIPHER_SUITE_CCMP_256, }; - int ret; - u32 ht_cap = 0; + int ret, i, j; + u32 ht_cap = U32_MAX, antennas_rx = 0, antennas_tx = 0; + bool is_6ghz = false, is_raw_mode = false, is_monitor_disable = false; + u8 *mac_addr = NULL; + u8 mbssid_max_interfaces = 0; - pdev = ar->pdev; + wiphy->max_ap_assoc_sta = 0; - if (ab->pdevs_macaddr_valid) - ether_addr_copy(ar->mac_addr, pdev->mac_addr); - else - ether_addr_copy(ar->mac_addr, ab->mac_addr); + for_each_ar(ah, ar, i) { + u32 ht_cap_info = 0; - ret = ath12k_mac_setup_register(ar, &ht_cap, hw->wiphy->bands); - if (ret) - goto out; + pdev = ar->pdev; + if (ar->ab->pdevs_macaddr_valid) { + ether_addr_copy(ar->mac_addr, pdev->mac_addr); + } else { + ether_addr_copy(ar->mac_addr, ar->ab->mac_addr); + ar->mac_addr[4] += ar->pdev_idx; + } + + ret = ath12k_mac_setup_register(ar, &ht_cap_info, hw->wiphy->bands); + if (ret) + goto err_cleanup_unregister; + + /* 6 GHz does not support HT Cap, hence do not consider it */ + if (!ar->supports_6ghz) + ht_cap &= ht_cap_info; + + wiphy->max_ap_assoc_sta += ar->max_num_stations; + + /* Advertise the max antenna support of all radios, driver can handle + * per pdev specific antenna setting based on pdev cap when antenna + * changes are made + */ + cap = &pdev->cap; + + antennas_rx = max_t(u32, antennas_rx, cap->rx_chain_mask); + antennas_tx = max_t(u32, antennas_tx, cap->tx_chain_mask); - wiphy->max_ap_assoc_sta = ar->max_num_stations; + if (ar->supports_6ghz) + is_6ghz = true; - cap = &pdev->cap; + if (test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) + is_raw_mode = true; - wiphy->available_antennas_rx = cap->rx_chain_mask; - wiphy->available_antennas_tx = cap->tx_chain_mask; + if (!ar->ab->hw_params->supports_monitor) + is_monitor_disable = true; - SET_IEEE80211_PERM_ADDR(hw, ar->mac_addr); + if (i == 0) + mac_addr = ar->mac_addr; + else + mac_addr = ab->mac_addr; + + mbssid_max_interfaces += TARGET_NUM_VDEVS; + } + + wiphy->available_antennas_rx = antennas_rx; + wiphy->available_antennas_tx = antennas_tx; + + SET_IEEE80211_PERM_ADDR(hw, mac_addr); SET_IEEE80211_DEV(hw, ab->dev); ret = ath12k_mac_setup_iface_combinations(ah); if (ret) { ath12k_err(ab, "failed to setup interface combinations: %d\n", ret); - goto err_cleanup_unregister; + goto err_complete_cleanup_unregister; } wiphy->interface_modes = ath12k_mac_get_ifmodes(ah); - if (wiphy->bands[NL80211_BAND_2GHZ] && + if (ah->num_radio == 1 && + wiphy->bands[NL80211_BAND_2GHZ] && wiphy->bands[NL80211_BAND_5GHZ] && wiphy->bands[NL80211_BAND_6GHZ]) ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); @@ -8018,8 +12355,9 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ieee80211_hw_set(hw, QUEUE_CONTROL); ieee80211_hw_set(hw, SUPPORTS_TX_FRAG); ieee80211_hw_set(hw, REPORTS_LOW_ACK); + ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR); - if (ht_cap & WMI_HT_CAP_ENABLED) { + if ((ht_cap & WMI_HT_CAP_ENABLED) || is_6ghz) { ieee80211_hw_set(hw, AMPDU_AGGREGATION); ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); @@ -8034,7 +12372,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) * for each band for a dual band capable radio. It will be tricky to * handle it when the ht capability different for each band. */ - if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS) + if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS || + (is_6ghz && ab->hw_params->supports_dynamic_smps_6ghz)) wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS; wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; @@ -8050,6 +12389,23 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | NL80211_FEATURE_AP_SCAN; + /* MLO is not yet supported so disable Wireless Extensions for now + * to make sure ath12k users don't use it. This flag can be removed + * once WIPHY_FLAG_SUPPORTS_MLO is enabled. + */ + wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; + + /* Copy over MLO related capabilities received from + * WMI_SERVICE_READY_EXT2_EVENT if single_chip_mlo_supp is set. + */ + if (ab->ag->mlo_capable) { + ath12k_iftypes_ext_capa[2].eml_capabilities = cap->eml_cap; + ath12k_iftypes_ext_capa[2].mld_capa_and_ops = cap->mld_cap; + wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + + ieee80211_hw_set(hw, MLO_MCAST_MULTI_LINK_TX); + } + hw->queues = ATH12K_HW_MAX_QUEUES; wiphy->tx_queue_len = ATH12K_QUEUE_LEN; hw->offchannel_tx_hw_queue = ATH12K_HW_MAX_QUEUES - 1; @@ -8057,6 +12413,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) hw->vif_data_size = sizeof(struct ath12k_vif); hw->sta_data_size = sizeof(struct ath12k_sta); + hw->extra_tx_headroom = ab->hw_params->iova_mask; wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); @@ -8067,7 +12424,10 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->iftype_ext_capab = ath12k_iftypes_ext_capa; wiphy->num_iftype_ext_capab = ARRAY_SIZE(ath12k_iftypes_ext_capa); - if (ar->supports_6ghz) { + wiphy->mbssid_max_interfaces = mbssid_max_interfaces; + wiphy->ema_max_profile_periodicity = TARGET_EMA_MAX_PROFILE_PERIOD; + + if (is_6ghz) { wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); wiphy_ext_feature_set(wiphy, @@ -8078,19 +12438,37 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ath12k_reg_init(hw); - if (!test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) { + if (!is_raw_mode) { hw->netdev_features = NETIF_F_HW_CSUM; ieee80211_hw_set(hw, SW_CRYPTO_CONTROL); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); } + if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { + wiphy->max_sched_scan_ssids = WMI_PNO_MAX_SUPP_NETWORKS; + wiphy->max_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; + wiphy->max_sched_scan_ie_len = WMI_PNO_MAX_IE_LENGTH; + wiphy->max_sched_scan_plans = WMI_PNO_MAX_SCHED_SCAN_PLANS; + wiphy->max_sched_scan_plan_interval = + WMI_PNO_MAX_SCHED_SCAN_PLAN_INT; + wiphy->max_sched_scan_plan_iterations = + WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS; + wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + } + + ret = ath12k_wow_init(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to init wow: %d\n", ret); + goto err_cleanup_if_combs; + } + ret = ieee80211_register_hw(hw); if (ret) { ath12k_err(ab, "ieee80211 registration failed: %d\n", ret); - goto err_free_if_combs; + goto err_cleanup_if_combs; } - if (!ab->hw_params->supports_monitor) + if (is_monitor_disable) /* There's a race between calling ieee80211_register_hw() * and here where the monitor mode is enabled for a little * while. But that time is so short and in practise it make @@ -8098,26 +12476,50 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) */ wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR); - /* Apply the regd received during initialization */ - ret = ath12k_regd_update(ar, true); - if (ret) { - ath12k_err(ar->ab, "ath12k regd update failed: %d\n", ret); - goto err_unregister_hw; + for_each_ar(ah, ar, i) { + /* Apply the regd received during initialization */ + ret = ath12k_regd_update(ar, true); + if (ret) { + ath12k_err(ar->ab, "ath12k regd update failed: %d\n", ret); + goto err_unregister_hw; + } + + if (ar->ab->hw_params->current_cc_support && ab->new_alpha2[0]) { + struct wmi_set_current_country_arg current_cc = {}; + + memcpy(¤t_cc.alpha2, ab->new_alpha2, 2); + memcpy(&ar->alpha2, ab->new_alpha2, 2); + ret = ath12k_wmi_send_set_current_country_cmd(ar, ¤t_cc); + if (ret) + ath12k_warn(ar->ab, + "failed set cc code for mac register: %d\n", + ret); + } + + ath12k_fw_stats_init(ar); + ath12k_debugfs_register(ar); } return 0; err_unregister_hw: + for_each_ar(ah, ar, i) + ath12k_debugfs_unregister(ar); + ieee80211_unregister_hw(hw); -err_free_if_combs: - kfree(wiphy->iface_combinations[0].limits); - kfree(wiphy->iface_combinations); +err_cleanup_if_combs: + ath12k_mac_cleanup_iface_combinations(ah); + +err_complete_cleanup_unregister: + i = ah->num_radio; err_cleanup_unregister: - ath12k_mac_cleanup_unregister(ar); + for (j = 0; j < i; j++) { + ar = ath12k_ah_to_ar(ah, j); + ath12k_mac_cleanup_unregister(ar); + } -out: SET_IEEE80211_DEV(hw, NULL); return ret; @@ -8141,11 +12543,13 @@ static void ath12k_mac_setup(struct ath12k *ar) ar->cfg_rx_chainmask = pdev->cap.rx_chain_mask; ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask); ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask); + ar->scan.arvif = NULL; + ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID; spin_lock_init(&ar->data_lock); INIT_LIST_HEAD(&ar->arvifs); INIT_LIST_HEAD(&ar->ppdu_stats_info); - mutex_init(&ar->conf_mutex); + init_completion(&ar->vdev_setup_done); init_completion(&ar->vdev_delete_done); init_completion(&ar->peer_assoc_done); @@ -8155,30 +12559,179 @@ static void ath12k_mac_setup(struct ath12k *ar) init_completion(&ar->scan.started); init_completion(&ar->scan.completed); init_completion(&ar->scan.on_channel); + init_completion(&ar->mlo_setup_done); + init_completion(&ar->completed_11d_scan); INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work); + wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work); INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work); - INIT_WORK(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work); + wiphy_work_init(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work); skb_queue_head_init(&ar->wmi_mgmt_tx_queue); - clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + + ar->monitor_vdev_id = -1; + ar->monitor_vdev_created = false; + ar->monitor_started = false; } -int ath12k_mac_register(struct ath12k_base *ab) +static int __ath12k_mac_mlo_setup(struct ath12k *ar) { - struct ath12k_hw *ah; - int i; + u8 num_link = 0, partner_link_id[ATH12K_GROUP_MAX_RADIO] = {}; + struct ath12k_base *partner_ab, *ab = ar->ab; + struct ath12k_hw_group *ag = ab->ag; + struct wmi_mlo_setup_arg mlo = {}; + struct ath12k_pdev *pdev; + unsigned long time_left; + int i, j, ret; + + lockdep_assert_held(&ag->mutex); + + reinit_completion(&ar->mlo_setup_done); + + for (i = 0; i < ag->num_devices; i++) { + partner_ab = ag->ab[i]; + + for (j = 0; j < partner_ab->num_radios; j++) { + pdev = &partner_ab->pdevs[j]; + + /* Avoid the self link */ + if (ar == pdev->ar) + continue; + + partner_link_id[num_link] = pdev->hw_link_id; + num_link++; + + ath12k_dbg(ab, ATH12K_DBG_MAC, "device %d pdev %d hw_link_id %d num_link %d\n", + i, j, pdev->hw_link_id, num_link); + } + } + + if (num_link == 0) + return 0; + + mlo.group_id = cpu_to_le32(ag->id); + mlo.partner_link_id = partner_link_id; + mlo.num_partner_links = num_link; + ar->mlo_setup_status = 0; + + ath12k_dbg(ab, ATH12K_DBG_MAC, "group id %d num_link %d\n", ag->id, num_link); + + ret = ath12k_wmi_mlo_setup(ar, &mlo); + if (ret) { + ath12k_err(ab, "failed to send setup MLO WMI command for pdev %d: %d\n", + ar->pdev_idx, ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->mlo_setup_done, + WMI_MLO_CMD_TIMEOUT_HZ); + + if (!time_left || ar->mlo_setup_status) + return ar->mlo_setup_status ? : -ETIMEDOUT; + + ath12k_dbg(ab, ATH12K_DBG_MAC, "mlo setup done for pdev %d\n", ar->pdev_idx); + + return 0; +} + +static int __ath12k_mac_mlo_teardown(struct ath12k *ar) +{ + struct ath12k_base *ab = ar->ab; int ret; + u8 num_link; - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) + if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) return 0; - /* Initialize channel counters frequency value in hertz */ - ab->cc_freq_hz = 320000; - ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + num_link = ath12k_get_num_partner_link(ar); + + if (num_link == 0) + return 0; + + ret = ath12k_wmi_mlo_teardown(ar); + if (ret) { + ath12k_warn(ab, "failed to send MLO teardown WMI command for pdev %d: %d\n", + ar->pdev_idx, ret); + return ret; + } + + ath12k_dbg(ab, ATH12K_DBG_MAC, "mlo teardown for pdev %d\n", ar->pdev_idx); + + return 0; +} + +int ath12k_mac_mlo_setup(struct ath12k_hw_group *ag) +{ + struct ath12k_hw *ah; + struct ath12k *ar; + int ret; + int i, j; + + for (i = 0; i < ag->num_hw; i++) { + ah = ag->ah[i]; + if (!ah) + continue; + + for_each_ar(ah, ar, j) { + ar = &ah->radio[j]; + ret = __ath12k_mac_mlo_setup(ar); + if (ret) { + ath12k_err(ar->ab, "failed to setup MLO: %d\n", ret); + goto err_setup; + } + } + } + + return 0; + +err_setup: + for (i = i - 1; i >= 0; i--) { + ah = ag->ah[i]; + if (!ah) + continue; + + for (j = j - 1; j >= 0; j--) { + ar = &ah->radio[j]; + if (!ar) + continue; + + __ath12k_mac_mlo_teardown(ar); + } + } - for (i = 0; i < ab->num_hw; i++) { - ah = ab->ah[i]; + return ret; +} + +void ath12k_mac_mlo_teardown(struct ath12k_hw_group *ag) +{ + struct ath12k_hw *ah; + struct ath12k *ar; + int ret, i, j; + + for (i = 0; i < ag->num_hw; i++) { + ah = ag->ah[i]; + if (!ah) + continue; + + for_each_ar(ah, ar, j) { + ar = &ah->radio[j]; + ret = __ath12k_mac_mlo_teardown(ar); + if (ret) { + ath12k_err(ar->ab, "failed to teardown MLO: %d\n", ret); + break; + } + } + } +} + +int ath12k_mac_register(struct ath12k_hw_group *ag) +{ + struct ath12k_hw *ah; + int i; + int ret; + + for (i = 0; i < ag->num_hw; i++) { + ah = ath12k_ag_to_ah(ag, i); ret = ath12k_mac_hw_register(ah); if (ret) @@ -8189,7 +12742,7 @@ int ath12k_mac_register(struct ath12k_base *ab) err: for (i = i - 1; i >= 0; i--) { - ah = ab->ah[i]; + ah = ath12k_ag_to_ah(ag, i); if (!ah) continue; @@ -8199,13 +12752,13 @@ err: return ret; } -void ath12k_mac_unregister(struct ath12k_base *ab) +void ath12k_mac_unregister(struct ath12k_hw_group *ag) { struct ath12k_hw *ah; int i; - for (i = ab->num_hw - 1; i >= 0; i--) { - ah = ab->ah[i]; + for (i = ag->num_hw - 1; i >= 0; i--) { + ah = ath12k_ag_to_ah(ag, i); if (!ah) continue; @@ -8218,12 +12771,13 @@ static void ath12k_mac_hw_destroy(struct ath12k_hw *ah) ieee80211_free_hw(ah->hw); } -static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_base *ab, +static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag, struct ath12k_pdev_map *pdev_map, u8 num_pdev_map) { struct ieee80211_hw *hw; struct ath12k *ar; + struct ath12k_base *ab; struct ath12k_pdev *pdev; struct ath12k_hw *ah; int i; @@ -8238,67 +12792,133 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_base *ab, ah->hw = hw; ah->num_radio = num_pdev_map; + mutex_init(&ah->hw_mutex); + INIT_LIST_HEAD(&ah->ml_peers); + for (i = 0; i < num_pdev_map; i++) { ab = pdev_map[i].ab; pdev_idx = pdev_map[i].pdev_idx; pdev = &ab->pdevs[pdev_idx]; - ar = ath12k_ah_to_ar(ah); + ar = ath12k_ah_to_ar(ah, i); ar->ah = ah; ar->ab = ab; - ar->hw_link_id = i; + ar->hw_link_id = pdev->hw_link_id; ar->pdev = pdev; ar->pdev_idx = pdev_idx; pdev->ar = ar; + ag->hw_links[ar->hw_link_id].device_id = ab->device_id; + ag->hw_links[ar->hw_link_id].pdev_idx = pdev_idx; + ath12k_mac_setup(ar); + ath12k_dp_pdev_pre_alloc(ar); } return ah; } -void ath12k_mac_destroy(struct ath12k_base *ab) +void ath12k_mac_destroy(struct ath12k_hw_group *ag) { struct ath12k_pdev *pdev; - int i; + struct ath12k_base *ab = ag->ab[0]; + int i, j; + struct ath12k_hw *ah; - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - if (!pdev->ar) + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) continue; - pdev->ar = NULL; + for (j = 0; j < ab->num_radios; j++) { + pdev = &ab->pdevs[j]; + if (!pdev->ar) + continue; + pdev->ar = NULL; + } } - for (i = 0; i < ab->num_hw; i++) { - if (!ab->ah[i]) + for (i = 0; i < ag->num_hw; i++) { + ah = ath12k_ag_to_ah(ag, i); + if (!ah) continue; - ath12k_mac_hw_destroy(ab->ah[i]); - ab->ah[i] = NULL; + ath12k_mac_hw_destroy(ah); + ath12k_ag_set_ah(ag, i, NULL); } } -int ath12k_mac_allocate(struct ath12k_base *ab) +static void ath12k_mac_set_device_defaults(struct ath12k_base *ab) +{ + /* Initialize channel counters frequency value in hertz */ + ab->cc_freq_hz = 320000; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; +} + +int ath12k_mac_allocate(struct ath12k_hw_group *ag) { + struct ath12k_pdev_map pdev_map[ATH12K_GROUP_MAX_RADIO]; + int mac_id, device_id, total_radio, num_hw; + struct ath12k_base *ab; struct ath12k_hw *ah; - struct ath12k_pdev_map pdev_map[MAX_RADIOS]; int ret, i, j; u8 radio_per_hw; - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) - return 0; + total_radio = 0; + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + ath12k_debugfs_pdev_create(ab); + ath12k_mac_set_device_defaults(ab); + total_radio += ab->num_radios; + } + + if (!total_radio) + return -EINVAL; + + if (WARN_ON(total_radio > ATH12K_GROUP_MAX_RADIO)) + return -ENOSPC; + + /* All pdev get combined and register as single wiphy based on + * hardware group which participate in multi-link operation else + * each pdev get register separately. + */ + if (ag->mlo_capable) + radio_per_hw = total_radio; + else + radio_per_hw = 1; - ab->num_hw = ab->num_radios; - radio_per_hw = 1; + num_hw = total_radio / radio_per_hw; - for (i = 0; i < ab->num_hw; i++) { + ag->num_hw = 0; + device_id = 0; + mac_id = 0; + for (i = 0; i < num_hw; i++) { for (j = 0; j < radio_per_hw; j++) { + if (device_id >= ag->num_devices || !ag->ab[device_id]) { + ret = -ENOSPC; + goto err; + } + + ab = ag->ab[device_id]; pdev_map[j].ab = ab; - pdev_map[j].pdev_idx = (i * radio_per_hw) + j; + pdev_map[j].pdev_idx = mac_id; + mac_id++; + + /* If mac_id falls beyond the current device MACs then + * move to next device + */ + if (mac_id >= ab->num_radios) { + mac_id = 0; + device_id++; + } } - ah = ath12k_mac_hw_allocate(ab, pdev_map, radio_per_hw); + ab = pdev_map->ab; + + ah = ath12k_mac_hw_allocate(ag, pdev_map, radio_per_hw); if (!ah) { ath12k_warn(ab, "failed to allocate mac80211 hw device for hw_idx %d\n", i); @@ -8306,21 +12926,54 @@ int ath12k_mac_allocate(struct ath12k_base *ab) goto err; } - ab->ah[i] = ah; - } + ah->dev = ab->dev; - ath12k_dp_pdev_pre_alloc(ab); + ag->ah[i] = ah; + ag->num_hw++; + } return 0; err: for (i = i - 1; i >= 0; i--) { - if (!ab->ah[i]) + ah = ath12k_ag_to_ah(ag, i); + if (!ah) continue; - ath12k_mac_hw_destroy(ab->ah[i]); - ab->ah[i] = NULL; + ath12k_mac_hw_destroy(ah); + ath12k_ag_set_ah(ag, i, NULL); } return ret; } + +int ath12k_mac_vif_set_keepalive(struct ath12k_link_vif *arvif, + enum wmi_sta_keepalive_method method, + u32 interval) +{ + struct wmi_sta_keepalive_arg arg = {}; + struct ath12k *ar = arvif->ar; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (arvif->ahvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + if (!test_bit(WMI_TLV_SERVICE_STA_KEEP_ALIVE, ar->ab->wmi_ab.svc_map)) + return 0; + + arg.vdev_id = arvif->vdev_id; + arg.enabled = 1; + arg.method = method; + arg.interval = interval; + + ret = ath12k_wmi_sta_keepalive(ar, &arg); + if (ret) { + ath12k_warn(ar->ab, "failed to set keepalive on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 3f5e1be0dff9..cc81b1f5680f 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_MAC_H @@ -9,10 +9,12 @@ #include <net/mac80211.h> #include <net/cfg80211.h> +#include "wmi.h" struct ath12k; struct ath12k_base; struct ath12k_hw; +struct ath12k_hw_group; struct ath12k_pdev_map; struct ath12k_generic_iter { @@ -31,6 +33,9 @@ struct ath12k_generic_iter { #define ATH12K_KEEPALIVE_MAX_IDLE 3895 #define ATH12K_KEEPALIVE_MAX_UNRESPONSIVE 3900 +#define ATH12K_PDEV_TX_POWER_INVALID ((u32)-1) +#define ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS 5000 /* msecs */ + /* FIXME: should these be in ieee80211.h? */ #define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16) #define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24) @@ -40,6 +45,17 @@ struct ath12k_generic_iter { #define ATH12K_TX_POWER_MAX_VAL 70 #define ATH12K_TX_POWER_MIN_VAL 0 +#define ATH12K_DEFAULT_LINK_ID 0 +#define ATH12K_INVALID_LINK_ID 255 + +/* Default link after the IEEE802.11 defined Max link id limit + * for driver usage purpose. + */ +#define ATH12K_DEFAULT_SCAN_LINK IEEE80211_MLD_MAX_NUM_LINKS +#define ATH12K_NUM_MAX_LINKS (IEEE80211_MLD_MAX_NUM_LINKS + 1) + +#define ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE 2 + enum ath12k_supported_bw { ATH12K_BW_20 = 0, ATH12K_BW_40 = 1, @@ -48,12 +64,64 @@ enum ath12k_supported_bw { ATH12K_BW_320 = 4, }; +struct ath12k_mac_get_any_chanctx_conf_arg { + struct ath12k *ar; + struct ieee80211_chanctx_conf *chanctx_conf; +}; + +/** + * struct ath12k_chan_power_info - TPE containing power info per channel chunk + * @chan_cfreq: channel center freq (MHz) + * e.g. + * channel 37/20 MHz, it is 6135 + * channel 37/40 MHz, it is 6125 + * channel 37/80 MHz, it is 6145 + * channel 37/160 MHz, it is 6185 + * @tx_power: transmit power (dBm) + */ +struct ath12k_chan_power_info { + u16 chan_cfreq; + s8 tx_power; +}; + +/* ath12k only deals with 320 MHz, so 16 subchannels */ +#define ATH12K_NUM_PWR_LEVELS 16 + +/** + * struct ath12k_reg_tpc_power_info - regulatory TPC power info + * @is_psd_power: is PSD power or not + * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD + * @ap_power_type: type of power (SP/LPI/VLP) + * @num_pwr_levels: number of power levels + * @reg_max: Array of maximum TX power (dBm) per PSD value + * @ap_constraint_power: AP constraint power (dBm) + * @tpe: TPE values processed from TPE IE + * @chan_power_info: power info to send to firmware + */ +struct ath12k_reg_tpc_power_info { + bool is_psd_power; + u8 eirp_power; + enum wmi_reg_6g_ap_type ap_power_type; + u8 num_pwr_levels; + u8 reg_max[ATH12K_NUM_PWR_LEVELS]; + u8 ap_constraint_power; + s8 tpe[ATH12K_NUM_PWR_LEVELS]; + struct ath12k_chan_power_info chan_power_info[ATH12K_NUM_PWR_LEVELS]; +}; + extern const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default; -void ath12k_mac_destroy(struct ath12k_base *ab); -void ath12k_mac_unregister(struct ath12k_base *ab); -int ath12k_mac_register(struct ath12k_base *ab); -int ath12k_mac_allocate(struct ath12k_base *ab); +#define ATH12K_SCAN_11D_INTERVAL 600000 +#define ATH12K_11D_INVALID_VDEV_ID 0xFFFF + +void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id); +void ath12k_mac_11d_scan_stop(struct ath12k *ar); +void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab); + +void ath12k_mac_destroy(struct ath12k_hw_group *ag); +void ath12k_mac_unregister(struct ath12k_hw_group *ag); +int ath12k_mac_register(struct ath12k_hw_group *ag); +int ath12k_mac_allocate(struct ath12k_hw_group *ag); int ath12k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, u16 *rate); u8 ath12k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, @@ -64,9 +132,9 @@ u8 ath12k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, void __ath12k_mac_scan_finish(struct ath12k *ar); void ath12k_mac_scan_finish(struct ath12k *ar); -struct ath12k_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id); -struct ath12k_vif *ath12k_mac_get_arvif_by_vdev_id(struct ath12k_base *ab, - u32 vdev_id); +struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id); +struct ath12k_link_vif *ath12k_mac_get_arvif_by_vdev_id(struct ath12k_base *ab, + u32 vdev_id); struct ath12k *ath12k_mac_get_ar_by_vdev_id(struct ath12k_base *ab, u32 vdev_id); struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id); @@ -78,4 +146,31 @@ enum ath12k_supported_bw ath12k_mac_mac80211_bw_to_ath12k_bw(enum rate_info_bw b enum hal_encrypt_type ath12k_dp_tx_get_encrypt_type(u32 cipher); int ath12k_mac_rfkill_enable_radio(struct ath12k *ar, bool enable); int ath12k_mac_rfkill_config(struct ath12k *ar); +int ath12k_mac_wait_tx_complete(struct ath12k *ar); +void ath12k_mac_handle_beacon(struct ath12k *ar, struct sk_buff *skb); +void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id); +int ath12k_mac_vif_set_keepalive(struct ath12k_link_vif *arvif, + enum wmi_sta_keepalive_method method, + u32 interval); +u8 ath12k_mac_get_target_pdev_id(struct ath12k *ar); +int ath12k_mac_mlo_setup(struct ath12k_hw_group *ag); +int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag); +void ath12k_mac_mlo_teardown(struct ath12k_hw_group *ag); +int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif); +void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + void *data); +u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones); +enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones); +enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi); +struct ieee80211_bss_conf *ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif); +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 link_id); +int ath12k_mac_get_fw_stats(struct ath12k *ar, struct ath12k_fw_stats_req_params *param); +void ath12k_mac_update_freq_range(struct ath12k *ar, + u32 freq_low, u32 freq_high); +void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ieee80211_chanctx_conf *ctx); #endif diff --git a/drivers/net/wireless/ath/ath12k/mhi.c b/drivers/net/wireless/ath/ath12k/mhi.c index adb8c3ec1950..08f44baf182a 100644 --- a/drivers/net/wireless/ath/ath12k/mhi.c +++ b/drivers/net/wireless/ath/ath12k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/msi.h> @@ -16,37 +16,10 @@ #define MHI_TIMEOUT_DEFAULT_MS 90000 #define OTP_INVALID_BOARD_ID 0xFFFF #define OTP_VALID_DUALMAC_BOARD_ID_MASK 0x1000 +#define MHI_CB_INVALID 0xff static const struct mhi_channel_config ath12k_mhi_channels_qcn9274[] = { { - .num = 0, - .name = "LOOPBACK", - .num_elements = 32, - .event_ring = 1, - .dir = DMA_TO_DEVICE, - .ee_mask = 0x4, - .pollcfg = 0, - .doorbell = MHI_DB_BRST_DISABLE, - .lpm_notify = false, - .offload_channel = false, - .doorbell_mode_switch = false, - .auto_queue = false, - }, - { - .num = 1, - .name = "LOOPBACK", - .num_elements = 32, - .event_ring = 1, - .dir = DMA_FROM_DEVICE, - .ee_mask = 0x4, - .pollcfg = 0, - .doorbell = MHI_DB_BRST_DISABLE, - .lpm_notify = false, - .offload_channel = false, - .doorbell_mode_switch = false, - .auto_queue = false, - }, - { .num = 20, .name = "IPCR", .num_elements = 32, @@ -112,34 +85,6 @@ const struct mhi_controller_config ath12k_mhi_config_qcn9274 = { static const struct mhi_channel_config ath12k_mhi_channels_wcn7850[] = { { - .num = 0, - .name = "LOOPBACK", - .num_elements = 32, - .event_ring = 0, - .dir = DMA_TO_DEVICE, - .ee_mask = 0x4, - .pollcfg = 0, - .doorbell = MHI_DB_BRST_DISABLE, - .lpm_notify = false, - .offload_channel = false, - .doorbell_mode_switch = false, - .auto_queue = false, - }, - { - .num = 1, - .name = "LOOPBACK", - .num_elements = 32, - .event_ring = 0, - .dir = DMA_FROM_DEVICE, - .ee_mask = 0x4, - .pollcfg = 0, - .doorbell = MHI_DB_BRST_DISABLE, - .lpm_notify = false, - .offload_channel = false, - .doorbell_mode_switch = false, - .auto_queue = false, - }, - { .num = 20, .name = "IPCR", .num_elements = 64, @@ -196,7 +141,7 @@ const struct mhi_controller_config ath12k_mhi_config_wcn7850 = { .max_channels = 128, .timeout_ms = 2000, .use_bounce_buf = false, - .buf_len = 0, + .buf_len = 8192, .num_channels = ARRAY_SIZE(ath12k_mhi_channels_wcn7850), .ch_cfg = ath12k_mhi_channels_wcn7850, .num_events = ARRAY_SIZE(ath12k_mhi_events_wcn7850), @@ -324,6 +269,7 @@ static void ath12k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, enum mhi_callback cb) { struct ath12k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev); + struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); ath12k_dbg(ab, ATH12K_DBG_BOOT, "mhi notify status reason %s\n", ath12k_mhi_op_callback_to_str(cb)); @@ -333,12 +279,23 @@ static void ath12k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, ath12k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n"); break; case MHI_CB_EE_RDDM: - if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) + if (ab_pci->mhi_pre_cb == MHI_CB_EE_RDDM) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "do not queue again for consecutive RDDM event\n"); + break; + } + + if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) { + set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); + set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); queue_work(ab->workqueue_aux, &ab->reset_work); + } break; default: break; } + + ab_pci->mhi_pre_cb = cb; } static int ath12k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl, @@ -369,6 +326,7 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci) if (!mhi_ctrl) return -ENOMEM; + ab_pci->mhi_pre_cb = MHI_CB_INVALID; ab_pci->mhi_ctrl = mhi_ctrl; mhi_ctrl->cntrl_dev = ab->dev; mhi_ctrl->regs = ab->mem; @@ -385,7 +343,6 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci) "failed to read board id\n"); } else if (board_id & OTP_VALID_DUALMAC_BOARD_ID_MASK) { dualmac = true; - ab->slo_capable = false; ath12k_dbg(ab, ATH12K_DBG_BOOT, "dualmac fw selected for board id: %x\n", board_id); } @@ -425,7 +382,7 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci) mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; mhi_ctrl->iova_start = 0; - mhi_ctrl->iova_stop = 0xffffffff; + mhi_ctrl->iova_stop = ab_pci->dma_mask; mhi_ctrl->sbl_size = SZ_512K; mhi_ctrl->seg_len = SZ_512K; mhi_ctrl->fbc_download = true; @@ -470,6 +427,8 @@ static char *ath12k_mhi_state_to_str(enum ath12k_mhi_state mhi_state) return "POWER_ON"; case ATH12K_MHI_POWER_OFF: return "POWER_OFF"; + case ATH12K_MHI_POWER_OFF_KEEP_DEV: + return "POWER_OFF_KEEP_DEV"; case ATH12K_MHI_FORCE_POWER_OFF: return "FORCE_POWER_OFF"; case ATH12K_MHI_SUSPEND: @@ -501,6 +460,7 @@ static void ath12k_mhi_set_state_bit(struct ath12k_pci *ab_pci, set_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state); break; case ATH12K_MHI_POWER_OFF: + case ATH12K_MHI_POWER_OFF_KEEP_DEV: case ATH12K_MHI_FORCE_POWER_OFF: clear_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state); clear_bit(ATH12K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state); @@ -544,6 +504,7 @@ static int ath12k_mhi_check_state_bit(struct ath12k_pci *ab_pci, return 0; break; case ATH12K_MHI_POWER_OFF: + case ATH12K_MHI_POWER_OFF_KEEP_DEV: case ATH12K_MHI_SUSPEND: if (test_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state) && !test_bit(ATH12K_MHI_SUSPEND, &ab_pci->mhi_state)) @@ -594,12 +555,27 @@ static int ath12k_mhi_set_state(struct ath12k_pci *ab_pci, ret = 0; break; case ATH12K_MHI_POWER_ON: - ret = mhi_async_power_up(ab_pci->mhi_ctrl); + /* In case of resume, QRTR's resume_early() is called + * right after ath12k' resume_early(). Since QRTR requires + * MHI mission mode state when preparing IPCR channels + * (see ee_mask of that channel), we need to use the 'sync' + * version here to make sure MHI is in that state when we + * return. Or QRTR might resume before that state comes, + * and as a result it fails. + * + * The 'sync' version works for non-resume (normal power on) + * case as well. + */ + ret = mhi_sync_power_up(ab_pci->mhi_ctrl); break; case ATH12K_MHI_POWER_OFF: mhi_power_down(ab_pci->mhi_ctrl, true); ret = 0; break; + case ATH12K_MHI_POWER_OFF_KEEP_DEV: + mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true); + ret = 0; + break; case ATH12K_MHI_FORCE_POWER_OFF: mhi_power_down(ab_pci->mhi_ctrl, false); ret = 0; @@ -653,9 +629,17 @@ out: return ret; } -void ath12k_mhi_stop(struct ath12k_pci *ab_pci) +void ath12k_mhi_stop(struct ath12k_pci *ab_pci, bool is_suspend) { - ath12k_mhi_set_state(ab_pci, ATH12K_MHI_POWER_OFF); + /* During suspend we need to use mhi_power_down_keep_dev() + * workaround, otherwise ath12k_core_resume() will timeout + * during resume. + */ + if (is_suspend) + ath12k_mhi_set_state(ab_pci, ATH12K_MHI_POWER_OFF_KEEP_DEV); + else + ath12k_mhi_set_state(ab_pci, ATH12K_MHI_POWER_OFF); + ath12k_mhi_set_state(ab_pci, ATH12K_MHI_DEINIT); } @@ -668,3 +652,8 @@ void ath12k_mhi_resume(struct ath12k_pci *ab_pci) { ath12k_mhi_set_state(ab_pci, ATH12K_MHI_RESUME); } + +void ath12k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic) +{ + mhi_download_rddm_image(mhi_ctrl, in_panic); +} diff --git a/drivers/net/wireless/ath/ath12k/mhi.h b/drivers/net/wireless/ath/ath12k/mhi.h index ebc23640ce7a..7358b8477536 100644 --- a/drivers/net/wireless/ath/ath12k/mhi.h +++ b/drivers/net/wireless/ath/ath12k/mhi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_MHI_H #define _ATH12K_MHI_H @@ -22,6 +22,7 @@ enum ath12k_mhi_state { ATH12K_MHI_DEINIT, ATH12K_MHI_POWER_ON, ATH12K_MHI_POWER_OFF, + ATH12K_MHI_POWER_OFF_KEEP_DEV, ATH12K_MHI_FORCE_POWER_OFF, ATH12K_MHI_SUSPEND, ATH12K_MHI_RESUME, @@ -34,7 +35,7 @@ extern const struct mhi_controller_config ath12k_mhi_config_qcn9274; extern const struct mhi_controller_config ath12k_mhi_config_wcn7850; int ath12k_mhi_start(struct ath12k_pci *ar_pci); -void ath12k_mhi_stop(struct ath12k_pci *ar_pci); +void ath12k_mhi_stop(struct ath12k_pci *ar_pci, bool is_suspend); int ath12k_mhi_register(struct ath12k_pci *ar_pci); void ath12k_mhi_unregister(struct ath12k_pci *ar_pci); void ath12k_mhi_set_mhictrl_reset(struct ath12k_base *ab); @@ -42,5 +43,5 @@ void ath12k_mhi_clear_vector(struct ath12k_base *ab); void ath12k_mhi_suspend(struct ath12k_pci *ar_pci); void ath12k_mhi_resume(struct ath12k_pci *ar_pci); - +void ath12k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic); #endif diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c index d334df720032..84cccf7d91e7 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.c +++ b/drivers/net/wireless/ath/ath12k/p2p.c @@ -69,20 +69,20 @@ static size_t ath12k_p2p_noa_ie_len_compute(const struct ath12k_wmi_p2p_noa_info return len; } -static void ath12k_p2p_noa_ie_assign(struct ath12k_vif *arvif, void *ie, +static void ath12k_p2p_noa_ie_assign(struct ath12k_link_vif *arvif, void *ie, size_t len) { struct ath12k *ar = arvif->ar; lockdep_assert_held(&ar->data_lock); - kfree(arvif->u.ap.noa_data); + kfree(arvif->ahvif->u.ap.noa_data); - arvif->u.ap.noa_data = ie; - arvif->u.ap.noa_len = len; + arvif->ahvif->u.ap.noa_data = ie; + arvif->ahvif->u.ap.noa_len = len; } -static void __ath12k_p2p_noa_update(struct ath12k_vif *arvif, +static void __ath12k_p2p_noa_update(struct ath12k_link_vif *arvif, const struct ath12k_wmi_p2p_noa_info *noa) { struct ath12k *ar = arvif->ar; @@ -105,7 +105,7 @@ static void __ath12k_p2p_noa_update(struct ath12k_vif *arvif, ath12k_p2p_noa_ie_assign(arvif, ie, len); } -void ath12k_p2p_noa_update(struct ath12k_vif *arvif, +void ath12k_p2p_noa_update(struct ath12k_link_vif *arvif, const struct ath12k_wmi_p2p_noa_info *noa) { struct ath12k *ar = arvif->ar; @@ -118,10 +118,13 @@ void ath12k_p2p_noa_update(struct ath12k_vif *arvif, static void ath12k_p2p_noa_update_vdev_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_p2p_noa_arg *arg = data; + struct ath12k_link_vif *arvif; - if (arvif->vdev_id != arg->vdev_id) + WARN_ON(!rcu_read_lock_any_held()); + arvif = &ahvif->deflink; + if (arvif->ar != arg->ar || arvif->vdev_id != arg->vdev_id) return; ath12k_p2p_noa_update(arvif, arg->noa); @@ -132,6 +135,7 @@ void ath12k_p2p_noa_update_by_vdev_id(struct ath12k *ar, u32 vdev_id, { struct ath12k_p2p_noa_arg arg = { .vdev_id = vdev_id, + .ar = ar, .noa = noa, }; diff --git a/drivers/net/wireless/ath/ath12k/p2p.h b/drivers/net/wireless/ath/ath12k/p2p.h index 5768139a7844..03ee877e6d6b 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.h +++ b/drivers/net/wireless/ath/ath12k/p2p.h @@ -12,10 +12,11 @@ struct ath12k_wmi_p2p_noa_info; struct ath12k_p2p_noa_arg { u32 vdev_id; + struct ath12k *ar; const struct ath12k_wmi_p2p_noa_info *noa; }; -void ath12k_p2p_noa_update(struct ath12k_vif *arvif, +void ath12k_p2p_noa_update(struct ath12k_link_vif *arvif, const struct ath12k_wmi_p2p_noa_info *noa); void ath12k_p2p_noa_update_by_vdev_id(struct ath12k *ar, u32 vdev_id, const struct ath12k_wmi_p2p_noa_info *noa); diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 14954bc05144..1f3cfd9b89fd 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> #include <linux/msi.h> #include <linux/pci.h> +#include <linux/time.h> +#include <linux/vmalloc.h> #include "pci.h" #include "core.h" @@ -15,7 +17,7 @@ #include "debug.h" #define ATH12K_PCI_BAR_NUM 0 -#define ATH12K_PCI_DMA_MASK 32 +#define ATH12K_PCI_DMA_MASK 36 #define ATH12K_PCI_IRQ_CE0_OFFSET 3 @@ -290,10 +292,10 @@ static void ath12k_pci_enable_ltssm(struct ath12k_base *ab) ath12k_dbg(ab, ATH12K_DBG_PCI, "pci ltssm 0x%x\n", val); - val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST); + val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST(ab)); val |= GCC_GCC_PCIE_HOT_RST_VAL; - ath12k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST, val); - val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST); + ath12k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST(ab), val); + val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST(ab)); ath12k_dbg(ab, ATH12K_DBG_PCI, "pci pcie_hot_rst 0x%x\n", val); @@ -350,6 +352,7 @@ static void ath12k_pci_free_ext_irq(struct ath12k_base *ab) free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); netif_napi_del(&irq_grp->napi); + free_netdev(irq_grp->napi_ndev); } } @@ -424,9 +427,9 @@ static void ath12k_pci_sync_ce_irqs(struct ath12k_base *ab) } } -static void ath12k_pci_ce_tasklet(struct tasklet_struct *t) +static void ath12k_pci_ce_workqueue(struct work_struct *work) { - struct ath12k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq); + struct ath12k_ce_pipe *ce_pipe = from_work(ce_pipe, work, intr_wq); int irq_idx = ATH12K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; ath12k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); @@ -448,7 +451,7 @@ static irqreturn_t ath12k_pci_ce_interrupt_handler(int irq, void *arg) disable_irq_nosync(ab->irq_num[irq_idx]); - tasklet_schedule(&ce_pipe->intr_tq); + queue_work(system_bh_wq, &ce_pipe->intr_wq); return IRQ_HANDLED; } @@ -472,15 +475,19 @@ static void __ath12k_pci_ext_irq_disable(struct ath12k_base *ab) { int i; - clear_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); + if (!test_and_clear_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) + return; for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; ath12k_pci_ext_grp_disable(irq_grp); - napi_synchronize(&irq_grp->napi); - napi_disable(&irq_grp->napi); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } } } @@ -560,8 +567,9 @@ static irqreturn_t ath12k_pci_ext_interrupt_handler(int irq, void *arg) static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) { struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); - int i, j, ret, num_vectors = 0; + int i, j, n, ret, num_vectors = 0; u32 user_base_data = 0, base_vector = 0, base_idx; + struct ath12k_ext_irq_grp *irq_grp; base_idx = ATH12K_PCI_IRQ_CE0_OFFSET + CE_COUNT_MAX; ret = ath12k_pci_get_user_msi_assignment(ab, "DP", @@ -572,13 +580,18 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) return ret; for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { - struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + irq_grp = &ab->ext_irq_grp[i]; u32 num_irq = 0; irq_grp->ab = ab; irq_grp->grp_id = i; - init_dummy_netdev(&irq_grp->napi_ndev); - netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + irq_grp->napi_ndev = alloc_netdev_dummy(0); + if (!irq_grp->napi_ndev) { + ret = -ENOMEM; + goto fail_allocate; + } + + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath12k_pci_ext_grp_napi_poll); if (ab->hw_params->ring_mask->tx[i] || @@ -587,7 +600,8 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) ab->hw_params->ring_mask->rx_wbm_rel[i] || ab->hw_params->ring_mask->reo_status[i] || ab->hw_params->ring_mask->host2rxdma[i] || - ab->hw_params->ring_mask->rx_mon_dest[i]) { + ab->hw_params->ring_mask->rx_mon_dest[i] || + ab->hw_params->ring_mask->rx_mon_status[i]) { num_irq = 1; } @@ -611,13 +625,23 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) if (ret) { ath12k_err(ab, "failed request irq %d: %d\n", vector, ret); - return ret; + goto fail_request; } } ath12k_pci_ext_grp_disable(irq_grp); } return 0; + +fail_request: + /* i ->napi_ndev was properly allocated. Free it also */ + i += 1; +fail_allocate: + for (n = 0; n < i; n++) { + irq_grp = &ab->ext_irq_grp[n]; + free_netdev(irq_grp->napi_ndev); + } + return ret; } static int ath12k_pci_set_irq_affinity_hint(struct ath12k_pci *ab_pci, @@ -626,7 +650,7 @@ static int ath12k_pci_set_irq_affinity_hint(struct ath12k_pci *ab_pci, if (test_bit(ATH12K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) return 0; - return irq_set_affinity_hint(ab_pci->pdev->irq, m); + return irq_set_affinity_and_hint(ab_pci->pdev->irq, m); } static int ath12k_pci_config_irq(struct ath12k_base *ab) @@ -657,7 +681,7 @@ static int ath12k_pci_config_irq(struct ath12k_base *ab) irq_idx = ATH12K_PCI_IRQ_CE0_OFFSET + i; - tasklet_setup(&ce_pipe->intr_tq, ath12k_pci_ce_tasklet); + INIT_WORK(&ce_pipe->intr_wq, ath12k_pci_ce_workqueue); ret = request_irq(irq, ath12k_pci_ce_interrupt_handler, ab_pci->irq_flags, irq_name[irq_idx], @@ -695,7 +719,7 @@ static void ath12k_pci_init_qmi_ce_config(struct ath12k_base *ab) cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len; ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id; - if (test_bit(ATH12K_FW_FEATURE_MULTI_QRTR_ID, ab->fw.fw_features)) { + if (ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MULTI_QRTR_ID)) { ab_pci->qmi_instance = u32_encode_bits(pci_domain_nr(bus), DOMAIN_NUMBER_MASK) | u32_encode_bits(bus->number, BUS_NUMBER_MASK); @@ -854,13 +878,9 @@ static int ath12k_pci_claim(struct ath12k_pci *ab_pci, struct pci_dev *pdev) goto disable_device; } - ret = dma_set_mask_and_coherent(&pdev->dev, - DMA_BIT_MASK(ATH12K_PCI_DMA_MASK)); - if (ret) { - ath12k_err(ab, "failed to set pci dma mask to %d: %d\n", - ATH12K_PCI_DMA_MASK, ret); - goto release_region; - } + ab_pci->dma_mask = DMA_BIT_MASK(ATH12K_PCI_DMA_MASK); + dma_set_mask(&pdev->dev, ab_pci->dma_mask); + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); pci_set_master(pdev); @@ -872,7 +892,7 @@ static int ath12k_pci_claim(struct ath12k_pci *ab_pci, struct pci_dev *pdev) goto release_region; } - ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot pci_mem 0x%pK\n", ab->mem); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot pci_mem 0x%p\n", ab->mem); return 0; release_region: @@ -936,14 +956,15 @@ static void ath12k_pci_update_qrtr_node_id(struct ath12k_base *ab) static void ath12k_pci_aspm_restore(struct ath12k_pci *ab_pci) { - if (test_and_clear_bit(ATH12K_PCI_ASPM_RESTORE, &ab_pci->flags)) + if (ab_pci->ab->hw_params->supports_aspm && + test_and_clear_bit(ATH12K_PCI_ASPM_RESTORE, &ab_pci->flags)) pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_ASPMC, ab_pci->link_ctl & PCI_EXP_LNKCTL_ASPMC); } -static void ath12k_pci_kill_tasklets(struct ath12k_base *ab) +static void ath12k_pci_cancel_workqueue(struct ath12k_base *ab) { int i; @@ -953,7 +974,7 @@ static void ath12k_pci_kill_tasklets(struct ath12k_base *ab) if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; - tasklet_kill(&ce_pipe->intr_tq); + cancel_work_sync(&ce_pipe->intr_wq); } } @@ -961,7 +982,7 @@ static void ath12k_pci_ce_irq_disable_sync(struct ath12k_base *ab) { ath12k_pci_ce_irqs_disable(ab); ath12k_pci_sync_ce_irqs(ab); - ath12k_pci_kill_tasklets(ab); + ath12k_pci_cancel_workqueue(ab); } int ath12k_pci_map_service_to_pipe(struct ath12k_base *ab, u16 service_id, @@ -1090,18 +1111,25 @@ void ath12k_pci_ext_irq_enable(struct ath12k_base *ab) { int i; - set_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); - for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; - napi_enable(&irq_grp->napi); + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } + ath12k_pci_ext_grp_enable(irq_grp); } + + set_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); } void ath12k_pci_ext_irq_disable(struct ath12k_base *ab) { + if (!test_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) + return; + __ath12k_pci_ext_irq_disable(ab); ath12k_pci_sync_ext_irqs(ab); } @@ -1126,6 +1154,11 @@ int ath12k_pci_hif_resume(struct ath12k_base *ab) void ath12k_pci_stop(struct ath12k_base *ab) { + struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + + if (!test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags)) + return; + ath12k_pci_ce_irq_disable_sync(ab); ath12k_ce_cleanup_pipes(ab); } @@ -1240,6 +1273,186 @@ void ath12k_pci_write32(struct ath12k_base *ab, u32 offset, u32 value) ab_pci->pci_ops->release(ab); } +#ifdef CONFIG_ATH12K_COREDUMP +static int ath12k_pci_coredump_calculate_size(struct ath12k_base *ab, u32 *dump_seg_sz) +{ + struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct ath12k_tlv_dump_data *dump_tlv; + enum ath12k_fw_crash_dump_type mem_type; + u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0; + struct ath12k_dump_file_data *file_data; + int i; + + rddm_img = mhi_ctrl->rddm_image; + if (!rddm_img) { + ath12k_err(ab, "No RDDM dump found\n"); + return 0; + } + + fw_img = mhi_ctrl->fbc_image; + + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + paging_tlv_sz += fw_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz; + + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + rddm_tlv_sz += rddm_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath12k_coredump_get_dump_type(ab->qmi.target_mem[i].type); + + if (mem_type == FW_CRASH_DUMP_NONE) + continue; + + if (mem_type == FW_CRASH_DUMP_TYPE_MAX) { + ath12k_dbg(ab, ATH12K_DBG_PCI, + "target mem region type %d not supported", + ab->qmi.target_mem[i].type); + continue; + } + + if (!ab->qmi.target_mem[i].paddr) + continue; + + dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size; + } + + for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) { + if (!dump_seg_sz[i]) + continue; + + len += sizeof(*dump_tlv) + dump_seg_sz[i]; + } + + if (len) + len += sizeof(*file_data); + + return len; +} + +static void ath12k_pci_coredump_download(struct ath12k_base *ab) +{ + struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct timespec64 timestamp; + int i, len, mem_idx; + enum ath12k_fw_crash_dump_type mem_type; + struct ath12k_dump_file_data *file_data; + struct ath12k_tlv_dump_data *dump_tlv; + size_t hdr_len = sizeof(*file_data); + void *buf; + u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = { 0 }; + + ath12k_mhi_coredump(mhi_ctrl, false); + + len = ath12k_pci_coredump_calculate_size(ab, dump_seg_sz); + if (!len) { + ath12k_warn(ab, "No crash dump data found for devcoredump"); + return; + } + + rddm_img = mhi_ctrl->rddm_image; + fw_img = mhi_ctrl->fbc_image; + + /* dev_coredumpv() requires vmalloc data */ + buf = vzalloc(len); + if (!buf) + return; + + ab->dump_data = buf; + ab->ath12k_coredump_len = len; + file_data = ab->dump_data; + strscpy(file_data->df_magic, "ATH12K-FW-DUMP", sizeof(file_data->df_magic)); + file_data->len = cpu_to_le32(len); + file_data->version = cpu_to_le32(ATH12K_FW_CRASH_DUMP_V2); + file_data->chip_id = cpu_to_le32(ab_pci->dev_id); + file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id); + file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus)); + guid_gen(&file_data->guid); + ktime_get_real_ts64(×tamp); + file_data->tv_sec = cpu_to_le64(timestamp.tv_sec); + file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec); + buf += hdr_len; + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf, + fw_img->mhi_buf[i].len); + buf += fw_img->mhi_buf[i].len; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf, + rddm_img->mhi_buf[i].len); + buf += rddm_img->mhi_buf[i].len; + } + + mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA; + for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) { + if (!dump_seg_sz[mem_idx] || mem_idx == FW_CRASH_DUMP_NONE) + continue; + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(mem_idx); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]); + buf += COREDUMP_TLV_HDR_SIZE; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath12k_coredump_get_dump_type + (ab->qmi.target_mem[i].type); + + if (mem_type != mem_idx) + continue; + + if (!ab->qmi.target_mem[i].paddr) { + ath12k_dbg(ab, ATH12K_DBG_PCI, + "Skipping mem region type %d", + ab->qmi.target_mem[i].type); + continue; + } + + memcpy_fromio(buf, ab->qmi.target_mem[i].v.ioaddr, + ab->qmi.target_mem[i].size); + buf += ab->qmi.target_mem[i].size; + } + } + + queue_work(ab->workqueue, &ab->dump_work); +} +#endif + int ath12k_pci_power_up(struct ath12k_base *ab) { struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); @@ -1256,7 +1469,7 @@ int ath12k_pci_power_up(struct ath12k_base *ab) ath12k_pci_msi_enable(ab_pci); - if (test_bit(ATH12K_FW_FEATURE_MULTI_QRTR_ID, ab->fw.fw_features)) + if (ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MULTI_QRTR_ID)) ath12k_pci_update_qrtr_node_id(ab); ret = ath12k_mhi_start(ab_pci); @@ -1271,20 +1484,30 @@ int ath12k_pci_power_up(struct ath12k_base *ab) return 0; } -void ath12k_pci_power_down(struct ath12k_base *ab) +void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend) { struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + if (!test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags)) + return; + /* restore aspm in case firmware bootup fails */ ath12k_pci_aspm_restore(ab_pci); ath12k_pci_force_wake(ab_pci->ab); ath12k_pci_msi_disable(ab_pci); - ath12k_mhi_stop(ab_pci); + ath12k_mhi_stop(ab_pci, is_suspend); clear_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags); ath12k_pci_sw_reset(ab_pci->ab, false); } +static int ath12k_pci_panic_handler(struct ath12k_base *ab) +{ + ath12k_pci_sw_reset(ab, false); + + return NOTIFY_OK; +} + static const struct ath12k_hif_ops ath12k_pci_hif_ops = { .start = ath12k_pci_start, .stop = ath12k_pci_stop, @@ -1302,6 +1525,10 @@ static const struct ath12k_hif_ops ath12k_pci_hif_ops = { .ce_irq_enable = ath12k_pci_hif_ce_irq_enable, .ce_irq_disable = ath12k_pci_hif_ce_irq_disable, .get_ce_msi_idx = ath12k_pci_get_ce_msi_idx, + .panic_handler = ath12k_pci_panic_handler, +#ifdef CONFIG_ATH12K_COREDUMP + .coredump_download = ath12k_pci_coredump_download, +#endif }; static @@ -1341,6 +1568,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab_pci->ab = ab; ab_pci->pdev = pdev; ab->hif.ops = &ath12k_pci_hif_ops; + ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL; pci_set_drvdata(pdev, ab); spin_lock_init(&ab_pci->window_lock); @@ -1469,6 +1697,8 @@ static int ath12k_pci_probe(struct pci_dev *pdev, return 0; err_free_irq: + /* __free_irq() expects the caller to have cleared the affinity hint */ + ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); ath12k_pci_free_irq(ab); err_ce_free: @@ -1480,12 +1710,12 @@ err_hal_srng_deinit: err_mhi_unregister: ath12k_mhi_unregister(ab_pci); -err_pci_msi_free: - ath12k_pci_msi_free(ab_pci); - err_irq_affinity_cleanup: ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); +err_pci_msi_free: + ath12k_pci_msi_free(ab_pci); + err_pci_free_region: ath12k_pci_free_region(ab_pci); @@ -1503,17 +1733,19 @@ static void ath12k_pci_remove(struct pci_dev *pdev) ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) { - ath12k_pci_power_down(ab); - ath12k_qmi_deinit_service(ab); + ath12k_pci_power_down(ab, false); goto qmi_fail; } set_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags); cancel_work_sync(&ab->reset_work); - ath12k_core_deinit(ab); + cancel_work_sync(&ab->dump_work); + ath12k_core_hw_group_cleanup(ab->ag); qmi_fail: + ath12k_core_deinit(ab); + ath12k_fw_unmap(ab); ath12k_mhi_unregister(ab_pci); ath12k_pci_free_irq(ab); @@ -1525,13 +1757,34 @@ qmi_fail: ath12k_core_free(ab); } +static void ath12k_pci_hw_group_power_down(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + if (!ag) + return; + + mutex_lock(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + ath12k_pci_power_down(ab, false); + } + + mutex_unlock(&ag->mutex); +} + static void ath12k_pci_shutdown(struct pci_dev *pdev) { struct ath12k_base *ab = pci_get_drvdata(pdev); struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); - ath12k_pci_power_down(ab); + ath12k_pci_hw_group_power_down(ab->ag); } static __maybe_unused int ath12k_pci_pm_suspend(struct device *dev) @@ -1558,9 +1811,36 @@ static __maybe_unused int ath12k_pci_pm_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(ath12k_pci_pm_ops, - ath12k_pci_pm_suspend, - ath12k_pci_pm_resume); +static __maybe_unused int ath12k_pci_pm_suspend_late(struct device *dev) +{ + struct ath12k_base *ab = dev_get_drvdata(dev); + int ret; + + ret = ath12k_core_suspend_late(ab); + if (ret) + ath12k_warn(ab, "failed to late suspend core: %d\n", ret); + + return ret; +} + +static __maybe_unused int ath12k_pci_pm_resume_early(struct device *dev) +{ + struct ath12k_base *ab = dev_get_drvdata(dev); + int ret; + + ret = ath12k_core_resume_early(ab); + if (ret) + ath12k_warn(ab, "failed to early resume core: %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops __maybe_unused ath12k_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ath12k_pci_pm_suspend, + ath12k_pci_pm_resume) + SET_LATE_SYSTEM_SLEEP_PM_OPS(ath12k_pci_pm_suspend_late, + ath12k_pci_pm_resume_early) +}; static struct pci_driver ath12k_pci_driver = { .name = "ath12k_pci", @@ -1571,7 +1851,7 @@ static struct pci_driver ath12k_pci_driver = { .driver.pm = &ath12k_pci_pm_ops, }; -static int ath12k_pci_init(void) +int ath12k_pci_init(void) { int ret; @@ -1584,14 +1864,8 @@ static int ath12k_pci_init(void) return 0; } -module_init(ath12k_pci_init); -static void ath12k_pci_exit(void) +void ath12k_pci_exit(void) { pci_unregister_driver(&ath12k_pci_driver); } - -module_exit(ath12k_pci_exit); - -MODULE_DESCRIPTION("Driver support for Qualcomm Technologies PCIe 802.11be WLAN devices"); -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h index ca93693ba4e9..d1ec8aad7f6c 100644 --- a/drivers/net/wireless/ath/ath12k/pci.h +++ b/drivers/net/wireless/ath/ath12k/pci.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_PCI_H #define ATH12K_PCI_H @@ -28,7 +28,9 @@ #define PCIE_PCIE_PARF_LTSSM 0x1e081b0 #define PARM_LTSSM_VALUE 0x111 -#define GCC_GCC_PCIE_HOT_RST 0x1e38338 +#define GCC_GCC_PCIE_HOT_RST(ab) \ + ((ab)->hw_params->regs->gcc_gcc_pcie_hot_rst) + #define GCC_GCC_PCIE_HOT_RST_VAL 0x10 #define PCIE_PCIE_INT_ALL_CLEAR 0x1e08228 @@ -104,6 +106,7 @@ struct ath12k_pci { struct mhi_controller *mhi_ctrl; const struct ath12k_msi_config *msi_config; unsigned long mhi_state; + enum mhi_callback mhi_pre_cb; u32 register_window; /* protects register_window above */ @@ -115,6 +118,7 @@ struct ath12k_pci { unsigned long irq_flags; const struct ath12k_pci_ops *pci_ops; u32 qmi_instance; + u64 dma_mask; }; static inline struct ath12k_pci *ath12k_pci_priv(struct ath12k_base *ab) @@ -143,5 +147,7 @@ int ath12k_pci_hif_resume(struct ath12k_base *ab); void ath12k_pci_stop(struct ath12k_base *ab); int ath12k_pci_start(struct ath12k_base *ab); int ath12k_pci_power_up(struct ath12k_base *ab); -void ath12k_pci_power_down(struct ath12k_base *ab); +void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend); +int ath12k_pci_init(void); +void ath12k_pci_exit(void); #endif /* ATH12K_PCI_H */ diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 19c0626fbff1..ec7236bbccc0 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -1,13 +1,29 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" #include "peer.h" #include "debug.h" +static struct ath12k_ml_peer *ath12k_peer_ml_find(struct ath12k_hw *ah, const u8 *addr) +{ + struct ath12k_ml_peer *ml_peer; + + lockdep_assert_wiphy(ah->hw->wiphy); + + list_for_each_entry(ml_peer, &ah->ml_peers, list) { + if (!ether_addr_equal(ml_peer->addr, addr)) + continue; + + return ml_peer; + } + + return NULL; +} + struct ath12k_peer *ath12k_peer_find(struct ath12k_base *ab, int vdev_id, const u8 *addr) { @@ -63,6 +79,20 @@ struct ath12k_peer *ath12k_peer_find_by_addr(struct ath12k_base *ab, return NULL; } +static struct ath12k_peer *ath12k_peer_find_by_ml_id(struct ath12k_base *ab, + int ml_peer_id) +{ + struct ath12k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) + if (ml_peer_id == peer->ml_id) + return peer; + + return NULL; +} + struct ath12k_peer *ath12k_peer_find_by_id(struct ath12k_base *ab, int peer_id) { @@ -70,6 +100,9 @@ struct ath12k_peer *ath12k_peer_find_by_id(struct ath12k_base *ab, lockdep_assert_held(&ab->base_lock); + if (peer_id & ATH12K_PEER_ML_ID_VALID) + return ath12k_peer_find_by_ml_id(ab, peer_id); + list_for_each_entry(peer, &ab->peers, list) if (peer_id == peer->peer_id) return peer; @@ -186,7 +219,7 @@ void ath12k_peer_cleanup(struct ath12k *ar, u32 vdev_id) struct ath12k_peer *peer, *tmp; struct ath12k_base *ab = ar->ab; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); spin_lock_bh(&ab->base_lock); list_for_each_entry_safe(peer, tmp, &ab->peers, list) { @@ -231,22 +264,36 @@ int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id, return 0; } -int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr) +static int ath12k_peer_delete_send(struct ath12k *ar, u32 vdev_id, const u8 *addr) { + struct ath12k_base *ab = ar->ab; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); reinit_completion(&ar->peer_delete_done); ret = ath12k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); if (ret) { - ath12k_warn(ar->ab, + ath12k_warn(ab, "failed to delete peer vdev_id %d addr %pM ret %d\n", vdev_id, addr, ret); return ret; } + return 0; +} + +int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr) +{ + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + ret = ath12k_peer_delete_send(ar, vdev_id, addr); + if (ret) + return ret; + ret = ath12k_wait_for_peer_delete_done(ar, vdev_id, addr); if (ret) return ret; @@ -261,14 +308,19 @@ static int ath12k_wait_for_peer_created(struct ath12k *ar, int vdev_id, const u8 return ath12k_wait_for_peer_common(ar->ab, vdev_id, addr, true); } -int ath12k_peer_create(struct ath12k *ar, struct ath12k_vif *arvif, +int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_sta *sta, struct ath12k_wmi_peer_create_arg *arg) { + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ath12k_link_sta *arsta; + u8 link_id = arvif->link_id; struct ath12k_peer *peer; + struct ath12k_sta *ahsta; + u16 ml_peer_id; int ret; - lockdep_assert_held(&ar->conf_mutex); + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); if (ar->num_peers > (ar->max_num_peers - 1)) { ath12k_warn(ar->ab, @@ -326,11 +378,37 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_vif *arvif, peer->pdev_idx = ar->pdev_idx; peer->sta = sta; - if (arvif->vif->type == NL80211_IFTYPE_STATION) { + if (vif->type == NL80211_IFTYPE_STATION) { arvif->ast_hash = peer->ast_hash; arvif->ast_idx = peer->hw_peer_id; } + if (vif->type == NL80211_IFTYPE_AP) + peer->ucast_ra_only = true; + + if (sta) { + ahsta = ath12k_sta_to_ahsta(sta); + arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + ahsta->link[link_id]); + + peer->link_id = arsta->link_id; + + /* Fill ML info into created peer */ + if (sta->mlo) { + ml_peer_id = ahsta->ml_peer_id; + peer->ml_id = ml_peer_id | ATH12K_PEER_ML_ID_VALID; + ether_addr_copy(peer->ml_addr, sta->addr); + + /* the assoc link is considered primary for now */ + peer->primary_link = arsta->is_assoc_link; + peer->mlo = true; + } else { + peer->ml_id = ATH12K_MLO_PEER_ID_INVALID; + peer->primary_link = true; + peer->mlo = false; + } + } + peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; @@ -340,3 +418,150 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_vif *arvif, return 0; } + +static u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah) +{ + u16 ml_peer_id; + + lockdep_assert_wiphy(ah->hw->wiphy); + + for (ml_peer_id = 0; ml_peer_id < ATH12K_MAX_MLO_PEERS; ml_peer_id++) { + if (test_bit(ml_peer_id, ah->free_ml_peer_id_map)) + continue; + + set_bit(ml_peer_id, ah->free_ml_peer_id_map); + break; + } + + if (ml_peer_id == ATH12K_MAX_MLO_PEERS) + ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; + + return ml_peer_id; +} + +int ath12k_peer_ml_create(struct ath12k_hw *ah, struct ieee80211_sta *sta) +{ + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_ml_peer *ml_peer; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (!sta->mlo) + return -EINVAL; + + ml_peer = ath12k_peer_ml_find(ah, sta->addr); + if (ml_peer) { + ath12k_hw_warn(ah, "ML peer %d exists already, unable to add new entry for %pM", + ml_peer->id, sta->addr); + return -EEXIST; + } + + ml_peer = kzalloc(sizeof(*ml_peer), GFP_ATOMIC); + if (!ml_peer) + return -ENOMEM; + + ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah); + + if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) { + ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM", + sta->addr); + kfree(ml_peer); + return -ENOMEM; + } + + ether_addr_copy(ml_peer->addr, sta->addr); + ml_peer->id = ahsta->ml_peer_id; + list_add(&ml_peer->list, &ah->ml_peers); + + return 0; +} + +int ath12k_peer_ml_delete(struct ath12k_hw *ah, struct ieee80211_sta *sta) +{ + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_ml_peer *ml_peer; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (!sta->mlo) + return -EINVAL; + + clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map); + ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; + + ml_peer = ath12k_peer_ml_find(ah, sta->addr); + if (!ml_peer) { + ath12k_hw_warn(ah, "ML peer for %pM not found", sta->addr); + return -EINVAL; + } + + list_del(&ml_peer->list); + kfree(ml_peer); + + return 0; +} + +int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta) +{ + struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); + struct ath12k_hw *ah = ahvif->ah; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + unsigned long links; + struct ath12k *ar; + int ret, err_ret = 0; + u8 link_id; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (!sta->mlo) + return -EINVAL; + + /* FW expects delete of all link peers at once before waiting for reception + * of peer unmap or delete responses + */ + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arvif || !arsta) + continue; + + ar = arvif->ar; + if (!ar) + continue; + + ath12k_dp_peer_cleanup(ar, arvif->vdev_id, arsta->addr); + + ret = ath12k_peer_delete_send(ar, arvif->vdev_id, arsta->addr); + if (ret) { + ath12k_warn(ar->ab, + "failed to delete peer vdev_id %d addr %pM ret %d\n", + arvif->vdev_id, arsta->addr, ret); + err_ret = ret; + continue; + } + } + + /* Ensure all link peers are deleted and unmapped */ + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arvif || !arsta) + continue; + + ar = arvif->ar; + if (!ar) + continue; + + ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arsta->addr); + if (ret) { + err_ret = ret; + continue; + } + ar->num_peers--; + } + + return err_ret; +} diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h index 7b3500b5c8c2..f3a5e054d2b5 100644 --- a/drivers/net/wireless/ath/ath12k/peer.h +++ b/drivers/net/wireless/ath/ath12k/peer.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_PEER_H @@ -19,6 +19,8 @@ struct ppdu_user_delayba { u32 resp_rate_flags; }; +#define ATH12K_PEER_ML_ID_VALID BIT(13) + struct ath12k_peer { struct list_head list; struct ieee80211_sta *sta; @@ -44,9 +46,29 @@ struct ath12k_peer { struct ppdu_user_delayba ppdu_stats_delayba; bool delayba_flag; bool is_authorized; - + bool mlo; /* protected by ab->data_lock */ bool dp_setup_done; + + u16 ml_id; + + /* any other ML info common for all partners can be added + * here and would be same for all partner peers. + */ + u8 ml_addr[ETH_ALEN]; + + /* To ensure only certain work related to dp is done once */ + bool primary_link; + + /* for reference to ath12k_link_sta */ + u8 link_id; + bool ucast_ra_only; +}; + +struct ath12k_ml_peer { + struct list_head list; + u8 addr[ETH_ALEN]; + u16 id; }; void ath12k_peer_unmap_event(struct ath12k_base *ab, u16 peer_id); @@ -59,12 +81,15 @@ struct ath12k_peer *ath12k_peer_find_by_addr(struct ath12k_base *ab, struct ath12k_peer *ath12k_peer_find_by_id(struct ath12k_base *ab, int peer_id); void ath12k_peer_cleanup(struct ath12k *ar, u32 vdev_id); int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr); -int ath12k_peer_create(struct ath12k *ar, struct ath12k_vif *arvif, +int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_sta *sta, struct ath12k_wmi_peer_create_arg *arg); int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id, const u8 *addr); bool ath12k_peer_exist_by_vdev_id(struct ath12k_base *ab, int vdev_id); struct ath12k_peer *ath12k_peer_find_by_ast(struct ath12k_base *ab, int ast_hash); +int ath12k_peer_ml_create(struct ath12k_hw *ah, struct ieee80211_sta *sta); +int ath12k_peer_ml_delete(struct ath12k_hw *ah, struct ieee80211_sta *sta); +int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta); #endif /* _PEER_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 92845ffff44a..99e1fb2910d0 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/elf.h> @@ -11,6 +11,8 @@ #include "debug.h" #include <linux/of.h> #include <linux/firmware.h> +#include <linux/of_address.h> +#include <linux/ioport.h> #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 #define HOST_CSTATE_BIT 0x04 @@ -583,6 +585,24 @@ static const struct qmi_elem_info qmi_wlanfw_phy_cap_resp_msg_v01_ei[] = { board_id), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + single_chip_mlo_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + single_chip_mlo_support), + }, + { .data_type = QMI_EOTI, .array_type = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, @@ -1998,49 +2018,145 @@ static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { }, }; -static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, - struct qmi_wlanfw_host_cap_req_msg_v01 *req) +static void ath12k_host_cap_hw_link_id_init(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab, *partner_ab; + int i, j, hw_id_base; + + for (i = 0; i < ag->num_devices; i++) { + hw_id_base = 0; + ab = ag->ab[i]; + + for (j = 0; j < ag->num_devices; j++) { + partner_ab = ag->ab[j]; + + if (partner_ab->wsi_info.index >= ab->wsi_info.index) + continue; + + hw_id_base += partner_ab->qmi.num_radios; + } + + ab->wsi_info.hw_link_id_base = hw_id_base; + } + + ag->hw_link_id_init_done = true; +} + +static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, + struct qmi_wlanfw_host_cap_req_msg_v01 *req) { struct wlfw_host_mlo_chip_info_s_v01 *info; + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_base *partner_ab; u8 hw_link_id = 0; - int i; + int i, j, ret; + + if (!ag->mlo_capable) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "MLO is disabled hence skip QMI MLO cap"); + return 0; + } if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) { + ag->mlo_capable = false; ath12k_dbg(ab, ATH12K_DBG_QMI, "skip QMI MLO cap due to invalid num_radio %d\n", ab->qmi.num_radios); - return; + return 0; + } + + if (ab->device_id == ATH12K_INVALID_DEVICE_ID) { + ath12k_err(ab, "failed to send MLO cap due to invalid device id\n"); + return -EINVAL; } req->mlo_capable_valid = 1; req->mlo_capable = 1; req->mlo_chip_id_valid = 1; - req->mlo_chip_id = 0; + req->mlo_chip_id = ab->device_id; req->mlo_group_id_valid = 1; - req->mlo_group_id = 0; + req->mlo_group_id = ag->id; req->max_mlo_peer_valid = 1; /* Max peer number generally won't change for the same device * but needs to be synced with host driver. */ req->max_mlo_peer = ab->hw_params->max_mlo_peer; req->mlo_num_chips_valid = 1; - req->mlo_num_chips = 1; + req->mlo_num_chips = ag->num_devices; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo capability advertisement device_id %d group_id %d num_devices %d", + req->mlo_chip_id, req->mlo_group_id, req->mlo_num_chips); - info = &req->mlo_chip_info[0]; - info->chip_id = 0; - info->num_local_links = ab->qmi.num_radios; + mutex_lock(&ag->mutex); - for (i = 0; i < info->num_local_links; i++) { - info->hw_link_id[i] = hw_link_id; - info->valid_mlo_link_id[i] = 1; + if (!ag->hw_link_id_init_done) + ath12k_host_cap_hw_link_id_init(ag); + + for (i = 0; i < ag->num_devices; i++) { + info = &req->mlo_chip_info[i]; + partner_ab = ag->ab[i]; + + if (partner_ab->device_id == ATH12K_INVALID_DEVICE_ID) { + ath12k_err(ab, "failed to send MLO cap due to invalid partner device id\n"); + ret = -EINVAL; + goto device_cleanup; + } - hw_link_id++; + info->chip_id = partner_ab->device_id; + info->num_local_links = partner_ab->qmi.num_radios; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo device id %d num_link %d\n", + info->chip_id, info->num_local_links); + + for (j = 0; j < info->num_local_links; j++) { + info->hw_link_id[j] = partner_ab->wsi_info.hw_link_id_base + j; + info->valid_mlo_link_id[j] = 1; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo hw_link_id %d\n", + info->hw_link_id[j]); + + hw_link_id++; + } } + if (hw_link_id <= 0) + ag->mlo_capable = false; + req->mlo_chip_info_valid = 1; + + mutex_unlock(&ag->mutex); + + return 0; + +device_cleanup: + for (i = i - 1; i >= 0; i--) { + info = &req->mlo_chip_info[i]; + + memset(info, 0, sizeof(*info)); + } + + req->mlo_num_chips = 0; + req->mlo_num_chips_valid = 0; + + req->max_mlo_peer = 0; + req->max_mlo_peer_valid = 0; + req->mlo_group_id = 0; + req->mlo_group_id_valid = 0; + req->mlo_chip_id = 0; + req->mlo_chip_id_valid = 0; + req->mlo_capable = 0; + req->mlo_capable_valid = 0; + + ag->mlo_capable = false; + + mutex_unlock(&ag->mutex); + + return ret; } -static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_host_cap_send(struct ath12k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req = {}; struct qmi_wlanfw_host_cap_resp_msg_v01 resp = {}; @@ -2054,10 +2170,12 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) req.bdf_support_valid = 1; req.bdf_support = 1; - req.m3_support_valid = 1; - req.m3_support = 1; - req.m3_cache_support_valid = 1; - req.m3_cache_support = 1; + if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) { + req.m3_support_valid = 1; + req.m3_support = 1; + req.m3_cache_support_valid = 1; + req.m3_cache_support = 1; + } req.cal_done_valid = 1; req.cal_done = ab->qmi.cal_done; @@ -2085,7 +2203,9 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) req.nm_modem |= PLATFORM_CAP_PCIE_GLOBAL_RESET; } - ath12k_host_cap_parse_mlo(ab, &req); + ret = ath12k_host_cap_parse_mlo(ab, &req); + if (ret < 0) + goto out; ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); @@ -2124,9 +2244,6 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) struct qmi_txn txn; int ret; - if (!ab->slo_capable) - goto out; - ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_phy_cap_resp_msg_v01_ei, &resp); if (ret < 0) @@ -2151,6 +2268,9 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) goto out; } + if (resp.single_chip_mlo_support_valid && resp.single_chip_mlo_support) + ab->single_chip_mlo_support = true; + if (!resp.num_phy_valid) { ret = -ENODATA; goto out; @@ -2158,7 +2278,9 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) ab->qmi.num_radios = resp.num_phy; - ath12k_dbg(ab, ATH12K_DBG_QMI, "phy capability resp valid %d num_phy %d valid %d board_id %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, + "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d\n", + resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support, resp.num_phy_valid, resp.num_phy, resp.board_id_valid, resp.board_id); @@ -2243,7 +2365,9 @@ resp_out: return ret; } -static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) { struct qmi_wlanfw_respond_mem_req_msg_v01 *req; struct qmi_wlanfw_respond_mem_resp_msg_v01 resp = {}; @@ -2260,7 +2384,8 @@ static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) * failure to firmware and firmware then request multiple blocks of * small chunk size memory. */ - if (ab->qmi.target_mem_delayed) { + if (!test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) && + ab->qmi.target_mem_delayed) { delayed = true; ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n", ab->qmi.mem_seg_count); @@ -2318,29 +2443,161 @@ out: return ret; } -static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) +void ath12k_qmi_reset_mlo_mem(struct ath12k_hw_group *ag) { + struct target_mem_chunk *mlo_chunk; int i; - for (i = 0; i < ab->qmi.mem_seg_count; i++) { - if (!ab->qmi.target_mem[i].v.addr) - continue; + lockdep_assert_held(&ag->mutex); + + if (!ag->mlo_mem.init_done || ag->num_started) + return; + + for (i = 0; i < ARRAY_SIZE(ag->mlo_mem.chunk); i++) { + mlo_chunk = &ag->mlo_mem.chunk[i]; + + if (mlo_chunk->v.addr) + /* TODO: Mode 0 recovery is the default mode hence resetting the + * whole memory region for now. Once Mode 1 support is added, this + * needs to be handled properly + */ + memset(mlo_chunk->v.addr, 0, mlo_chunk->size); + } +} + +static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, + struct target_mem_chunk *chunk, + int idx) +{ + struct ath12k_hw_group *ag = ab->ag; + struct target_mem_chunk *mlo_chunk; + bool fixed_mem; + + lockdep_assert_held(&ag->mutex); + + if (!ag->mlo_mem.init_done || ag->num_started) + return; + + if (idx >= ARRAY_SIZE(ag->mlo_mem.chunk)) { + ath12k_warn(ab, "invalid index for MLO memory chunk free: %d\n", idx); + return; + } + + fixed_mem = test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags); + mlo_chunk = &ag->mlo_mem.chunk[idx]; + + if (fixed_mem && mlo_chunk->v.ioaddr) { + iounmap(mlo_chunk->v.ioaddr); + mlo_chunk->v.ioaddr = NULL; + } else if (mlo_chunk->v.addr) { dma_free_coherent(ab->dev, - ab->qmi.target_mem[i].size, - ab->qmi.target_mem[i].v.addr, - ab->qmi.target_mem[i].paddr); - ab->qmi.target_mem[i].v.addr = NULL; + mlo_chunk->size, + mlo_chunk->v.addr, + mlo_chunk->paddr); + mlo_chunk->v.addr = NULL; } + + mlo_chunk->paddr = 0; + mlo_chunk->size = 0; + if (fixed_mem) + chunk->v.ioaddr = NULL; + else + chunk->v.addr = NULL; + chunk->paddr = 0; + chunk->size = 0; +} + +static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + int i, mlo_idx; + + for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) { + if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) { + ath12k_qmi_free_mlo_mem_chunk(ab, + &ab->qmi.target_mem[i], + mlo_idx++); + } else { + if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) && + ab->qmi.target_mem[i].v.ioaddr) { + iounmap(ab->qmi.target_mem[i].v.ioaddr); + ab->qmi.target_mem[i].v.ioaddr = NULL; + } else { + if (!ab->qmi.target_mem[i].v.addr) + continue; + dma_free_coherent(ab->dev, + ab->qmi.target_mem[i].prev_size, + ab->qmi.target_mem[i].v.addr, + ab->qmi.target_mem[i].paddr); + ab->qmi.target_mem[i].v.addr = NULL; + } + } + } + + if (!ag->num_started && ag->mlo_mem.init_done) { + ag->mlo_mem.init_done = false; + ag->mlo_mem.mlo_mem_size = 0; + } +} + +static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab, + struct target_mem_chunk *chunk) +{ + /* Firmware reloads in recovery/resume. + * In such cases, no need to allocate memory for FW again. + */ + if (chunk->v.addr) { + if (chunk->prev_type == chunk->type && + chunk->prev_size == chunk->size) + goto this_chunk_done; + + /* cannot reuse the existing chunk */ + dma_free_coherent(ab->dev, chunk->prev_size, + chunk->v.addr, chunk->paddr); + chunk->v.addr = NULL; + } + + chunk->v.addr = dma_alloc_coherent(ab->dev, + chunk->size, + &chunk->paddr, + GFP_KERNEL | __GFP_NOWARN); + if (!chunk->v.addr) { + if (chunk->size > ATH12K_QMI_MAX_CHUNK_SIZE) { + ab->qmi.target_mem_delayed = true; + ath12k_warn(ab, + "qmi dma allocation failed (%d B type %u), will try later with small size\n", + chunk->size, + chunk->type); + ath12k_qmi_free_target_mem_chunk(ab); + return -EAGAIN; + } + ath12k_warn(ab, "memory allocation failure for %u size: %d\n", + chunk->type, chunk->size); + return -ENOMEM; + } + chunk->prev_type = chunk->type; + chunk->prev_size = chunk->size; +this_chunk_done: + return 0; } static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) { - int i; - struct target_mem_chunk *chunk; + struct target_mem_chunk *chunk, *mlo_chunk; + struct ath12k_hw_group *ag = ab->ag; + int i, mlo_idx, ret; + int mlo_size = 0; + + mutex_lock(&ag->mutex); + + if (!ag->mlo_mem.init_done) { + memset(ag->mlo_mem.chunk, 0, sizeof(ag->mlo_mem.chunk)); + ag->mlo_mem.init_done = true; + } ab->qmi.target_mem_delayed = false; - for (i = 0; i < ab->qmi.mem_seg_count; i++) { + for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) { chunk = &ab->qmi.target_mem[i]; /* Allocate memory for the region and the functionality supported @@ -2352,24 +2609,41 @@ 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: - chunk->v.addr = dma_alloc_coherent(ab->dev, - chunk->size, - &chunk->paddr, - GFP_KERNEL | __GFP_NOWARN); - if (!chunk->v.addr) { - if (chunk->size > ATH12K_QMI_MAX_CHUNK_SIZE) { - ab->qmi.target_mem_delayed = true; - ath12k_warn(ab, - "qmi dma allocation failed (%d B type %u), will try later with small size\n", - chunk->size, - chunk->type); - ath12k_qmi_free_target_mem_chunk(ab); - return 0; + ret = ath12k_qmi_alloc_chunk(ab, chunk); + if (ret) + goto err; + break; + case MLO_GLOBAL_MEM_REGION_TYPE: + mlo_size += chunk->size; + if (ag->mlo_mem.mlo_mem_size && + mlo_size > ag->mlo_mem.mlo_mem_size) { + ath12k_err(ab, "QMI MLO memory allocation failure, requested size %d is more than allocated size %d", + mlo_size, ag->mlo_mem.mlo_mem_size); + ret = -EINVAL; + goto err; + } + + mlo_chunk = &ag->mlo_mem.chunk[mlo_idx]; + if (mlo_chunk->paddr) { + if (chunk->size != mlo_chunk->size) { + ath12k_err(ab, "QMI MLO chunk memory allocation failure for index %d, requested size %d is more than allocated size %d", + mlo_idx, chunk->size, mlo_chunk->size); + ret = -EINVAL; + goto err; } - ath12k_warn(ab, "memory allocation failure for %u size: %d\n", - chunk->type, chunk->size); - return -ENOMEM; + } else { + mlo_chunk->size = chunk->size; + mlo_chunk->type = chunk->type; + ret = ath12k_qmi_alloc_chunk(ab, mlo_chunk); + if (ret) + goto err; + memset(mlo_chunk->v.addr, 0, mlo_chunk->size); } + + chunk->paddr = mlo_chunk->paddr; + chunk->v.addr = mlo_chunk->v.addr; + mlo_idx++; + break; default: ath12k_warn(ab, "memory type %u not supported\n", @@ -2379,10 +2653,163 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) break; } } + + if (!ag->mlo_mem.mlo_mem_size) { + ag->mlo_mem.mlo_mem_size = mlo_size; + } else if (ag->mlo_mem.mlo_mem_size != mlo_size) { + ath12k_err(ab, "QMI MLO memory size error, expected size is %d but requested size is %d", + ag->mlo_mem.mlo_mem_size, mlo_size); + ret = -EINVAL; + goto err; + } + + mutex_unlock(&ag->mutex); + + return 0; + +err: + ath12k_qmi_free_target_mem_chunk(ab); + + mutex_unlock(&ag->mutex); + + /* The firmware will attempt to request memory in smaller chunks + * on the next try. However, the current caller should be notified + * that this instance of request parsing was successful. + * Therefore, return 0 only. + */ + if (ret == -EAGAIN) + ret = 0; + + return ret; +} + +static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab) +{ + struct reserved_mem *rmem; + size_t avail_rmem_size; + int i, idx, ret; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { + switch (ab->qmi.target_mem[i].type) { + case HOST_DDR_REGION_TYPE: + rmem = ath12k_core_get_reserved_mem(ab, 0); + if (!rmem) { + ret = -ENODEV; + goto out; + } + + avail_rmem_size = rmem->size; + if (avail_rmem_size < ab->qmi.target_mem[i].size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + ab->qmi.target_mem[i].type, + ab->qmi.target_mem[i].size, + avail_rmem_size); + ret = -EINVAL; + goto out; + } + + ab->qmi.target_mem[idx].paddr = rmem->base; + ab->qmi.target_mem[idx].v.ioaddr = + ioremap(ab->qmi.target_mem[idx].paddr, + ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case BDF_MEM_REGION_TYPE: + rmem = ath12k_core_get_reserved_mem(ab, 0); + if (!rmem) { + ret = -ENODEV; + goto out; + } + + avail_rmem_size = rmem->size - ab->hw_params->bdf_addr_offset; + if (avail_rmem_size < ab->qmi.target_mem[i].size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + ab->qmi.target_mem[i].type, + ab->qmi.target_mem[i].size, + avail_rmem_size); + ret = -EINVAL; + goto out; + } + ab->qmi.target_mem[idx].paddr = + rmem->base + ab->hw_params->bdf_addr_offset; + ab->qmi.target_mem[idx].v.ioaddr = + ioremap(ab->qmi.target_mem[idx].paddr, + ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case CALDB_MEM_REGION_TYPE: + /* Cold boot calibration is not enabled in Ath12k. Hence, + * assign paddr = 0. + * Once cold boot calibration is enabled add support to + * assign reserved memory from DT. + */ + ab->qmi.target_mem[idx].paddr = 0; + ab->qmi.target_mem[idx].v.ioaddr = NULL; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case M3_DUMP_REGION_TYPE: + rmem = ath12k_core_get_reserved_mem(ab, 1); + if (!rmem) { + ret = -EINVAL; + goto out; + } + + avail_rmem_size = rmem->size; + if (avail_rmem_size < ab->qmi.target_mem[i].size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + ab->qmi.target_mem[i].type, + ab->qmi.target_mem[i].size, + avail_rmem_size); + ret = -EINVAL; + goto out; + } + + ab->qmi.target_mem[idx].paddr = rmem->base; + ab->qmi.target_mem[idx].v.ioaddr = + ioremap(ab->qmi.target_mem[idx].paddr, + ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + default: + ath12k_warn(ab, "qmi ignore invalid mem req type %u\n", + ab->qmi.target_mem[i].type); + break; + } + } + ab->qmi.mem_seg_count = idx; + return 0; +out: + ath12k_qmi_free_target_mem_chunk(ab); + return ret; } -static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_request_target_cap(struct ath12k_base *ab) { struct qmi_wlanfw_cap_req_msg_v01 req = {}; struct qmi_wlanfw_cap_resp_msg_v01 resp = {}; @@ -2452,7 +2879,7 @@ static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) ab->qmi.dev_mem[i].size = resp.dev_mem[i].size; ath12k_dbg(ab, ATH12K_DBG_QMI, - "devmem [%d] start ox%llx size %llu\n", i, + "devmem [%d] start 0x%llx size %llu\n", i, ab->qmi.dev_mem[i].start, ab->qmi.dev_mem[i].size); } @@ -2476,6 +2903,15 @@ static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) if (r) ath12k_dbg(ab, ATH12K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); + r = ath12k_acpi_start(ab); + if (r) + /* ACPI is optional so continue in case of an error */ + ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", r); + + r = ath12k_acpi_check_bdf_variant_name(ab); + if (r) + ath12k_dbg(ab, ATH12K_DBG_BOOT, "ACPI bdf variant name not set.\n"); + out: return ret; } @@ -2487,7 +2923,7 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, struct qmi_wlanfw_bdf_download_resp_msg_v01 resp = {}; struct qmi_txn txn; const u8 *temp = data; - int ret; + int ret = 0; u32 remaining = len; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -2568,8 +3004,10 @@ out: return ret; } -static int ath12k_qmi_load_bdf_qmi(struct ath12k_base *ab, - enum ath12k_qmi_bdf_type type) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_load_bdf_qmi(struct ath12k_base *ab, + enum ath12k_qmi_bdf_type type) { struct device *dev = ab->dev; char filename[ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE]; @@ -2666,6 +3104,22 @@ out: return ret; } +static void ath12k_qmi_m3_free(struct ath12k_base *ab) +{ + struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; + + if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_remoteproc) + return; + + if (!m3_mem->vaddr) + return; + + dma_free_coherent(ab->dev, m3_mem->size, + m3_mem->vaddr, m3_mem->paddr); + m3_mem->vaddr = NULL; + m3_mem->size = 0; +} + static int ath12k_qmi_m3_load(struct ath12k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; @@ -2675,10 +3129,6 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab) size_t m3_len; int ret; - if (m3_mem->vaddr) - /* m3 firmware buffer is already available in the DMA buffer */ - return 0; - if (ab->fw.m3_data && ab->fw.m3_len > 0) { /* firmware-N.bin had a m3 firmware file so use that */ m3_data = ab->fw.m3_data; @@ -2700,6 +3150,15 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab) m3_len = fw->size; } + /* In recovery/resume cases, M3 buffer is not freed, try to reuse that */ + if (m3_mem->vaddr) { + if (m3_mem->size >= m3_len) + goto skip_m3_alloc; + + /* Old buffer is too small, free and reallocate */ + ath12k_qmi_m3_free(ab); + } + m3_mem->vaddr = dma_alloc_coherent(ab->dev, m3_len, &m3_mem->paddr, GFP_KERNEL); @@ -2710,6 +3169,7 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab) goto out; } +skip_m3_alloc: memcpy(m3_mem->vaddr, m3_data, m3_len); m3_mem->size = m3_len; @@ -2721,20 +3181,9 @@ out: return ret; } -static void ath12k_qmi_m3_free(struct ath12k_base *ab) -{ - struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; - - if (!m3_mem->vaddr) - return; - - dma_free_coherent(ab->dev, m3_mem->size, - m3_mem->vaddr, m3_mem->paddr); - m3_mem->vaddr = NULL; - m3_mem->size = 0; -} - -static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; struct qmi_wlanfw_m3_info_req_msg_v01 req = {}; @@ -2742,15 +3191,16 @@ static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) struct qmi_txn txn; int ret = 0; - ret = ath12k_qmi_m3_load(ab); - if (ret) { - ath12k_err(ab, "failed to load m3 firmware: %d", ret); - return ret; + if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) { + ret = ath12k_qmi_m3_load(ab); + if (ret) { + ath12k_err(ab, "failed to load m3 firmware: %d", ret); + return ret; + } + req.addr = m3_mem->paddr; + req.size = m3_mem->size; } - req.addr = m3_mem->paddr; - req.size = m3_mem->size; - ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_m3_info_resp_msg_v01_ei, &resp); if (ret < 0) @@ -2966,6 +3416,8 @@ void ath12k_qmi_firmware_stop(struct ath12k_base *ab) { int ret; + clear_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags); + ret = ath12k_qmi_wlanfw_mode_send(ab, ATH12K_FIRMWARE_MODE_OFF); if (ret < 0) { ath12k_warn(ab, "qmi failed to send wlan mode off\n"); @@ -3022,9 +3474,69 @@ ath12k_qmi_driver_event_post(struct ath12k_qmi *qmi, return 0; } -static int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) +void ath12k_qmi_trigger_host_cap(struct ath12k_base *ab) { - struct ath12k_base *ab = qmi->ab; + struct ath12k_qmi *qmi = &ab->qmi; + + spin_lock(&qmi->event_lock); + + if (ath12k_qmi_get_event_block(qmi)) + ath12k_qmi_set_event_block(qmi, false); + + spin_unlock(&qmi->event_lock); + + ath12k_dbg(ab, ATH12K_DBG_QMI, "trigger host cap for device id %d\n", + ab->device_id); + + ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_HOST_CAP, NULL); +} + +static bool ath12k_qmi_hw_group_host_cap_ready(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + + if (!(ab && ab->qmi.num_radios != U8_MAX)) + return false; + } + + return true; +} + +static struct ath12k_base *ath12k_qmi_hw_group_find_blocked(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + lockdep_assert_held(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + spin_lock(&ab->qmi.event_lock); + + if (ath12k_qmi_get_event_block(&ab->qmi)) { + spin_unlock(&ab->qmi.event_lock); + return ab; + } + + spin_unlock(&ab->qmi.event_lock); + } + + return NULL; +} + +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) +{ + struct ath12k_base *ab = qmi->ab, *block_ab; + struct ath12k_hw_group *ag = ab->ag; int ret; ath12k_qmi_phy_cap_send(ab); @@ -3035,16 +3547,30 @@ static int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) return ret; } - ret = ath12k_qmi_host_cap_send(ab); - if (ret < 0) { - ath12k_warn(ab, "qmi failed to send host cap QMI:%d\n", ret); - return ret; + spin_lock(&qmi->event_lock); + + ath12k_qmi_set_event_block(qmi, true); + + spin_unlock(&qmi->event_lock); + + mutex_lock(&ag->mutex); + + if (ath12k_qmi_hw_group_host_cap_ready(ag)) { + ath12k_core_hw_group_set_mlo_capable(ag); + + block_ab = ath12k_qmi_hw_group_find_blocked(ag); + if (block_ab) + ath12k_qmi_trigger_host_cap(block_ab); } + mutex_unlock(&ag->mutex); + return ret; } -static int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) { struct ath12k_base *ab = qmi->ab; int ret; @@ -3058,7 +3584,9 @@ static int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) return ret; } -static int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) { struct ath12k_base *ab = qmi->ab; int ret; @@ -3122,11 +3650,20 @@ static void ath12k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, msg->mem_seg[i].type, msg->mem_seg[i].size); } - ret = ath12k_qmi_alloc_target_mem_chunk(ab); - if (ret) { - ath12k_warn(ab, "qmi failed to alloc target memory: %d\n", - ret); - return; + if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags)) { + ret = ath12k_qmi_assign_target_mem_chunk(ab); + if (ret) { + ath12k_warn(ab, "failed to assign qmi target memory: %d\n", + ret); + return; + } + } else { + ret = ath12k_qmi_alloc_target_mem_chunk(ab); + if (ret) { + ath12k_warn(ab, "qmi failed to alloc target memory: %d\n", + ret); + return; + } } ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_REQUEST_MEM, NULL); @@ -3178,6 +3715,9 @@ static const struct qmi_msg_handler ath12k_qmi_msg_handlers[] = { .decoded_size = sizeof(struct qmi_wlanfw_fw_ready_ind_msg_v01), .fn = ath12k_qmi_msg_fw_ready_cb, }, + + /* end of list */ + {}, }; static int ath12k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, @@ -3220,6 +3760,21 @@ static const struct qmi_ops ath12k_qmi_ops = { .del_server = ath12k_qmi_ops_del_server, }; +static int ath12k_qmi_event_host_cap(struct ath12k_qmi *qmi) +{ + struct ath12k_base *ab = qmi->ab; + int ret; + + ret = ath12k_qmi_host_cap_send(ab); + if (ret < 0) { + ath12k_warn(ab, "failed to send qmi host cap for device id %d: %d\n", + ab->device_id, ret); + return ret; + } + + return ret; +} + static void ath12k_qmi_driver_event_work(struct work_struct *work) { struct ath12k_qmi *qmi = container_of(work, struct ath12k_qmi, @@ -3246,7 +3801,6 @@ static void ath12k_qmi_driver_event_work(struct work_struct *work) break; case ATH12K_QMI_EVENT_SERVER_EXIT: set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); - set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); break; case ATH12K_QMI_EVENT_REQUEST_MEM: ret = ath12k_qmi_event_mem_request(qmi); @@ -3260,19 +3814,28 @@ static void ath12k_qmi_driver_event_work(struct work_struct *work) break; case ATH12K_QMI_EVENT_FW_READY: clear_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags); - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { - ath12k_hal_dump_srng_stats(ab); + if (test_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags)) { + if (ab->is_reset) + ath12k_hal_dump_srng_stats(ab); + + set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); queue_work(ab->workqueue, &ab->restart_work); break; } clear_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); - clear_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); - ath12k_core_qmi_firmware_ready(ab); - set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); + ret = ath12k_core_qmi_firmware_ready(ab); + if (!ret) + set_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, + &ab->dev_flags); break; + case ATH12K_QMI_EVENT_HOST_CAP: + ret = ath12k_qmi_event_host_cap(qmi); + if (ret < 0) + set_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags); + break; default: ath12k_warn(ab, "invalid event type: %d", event->type); break; @@ -3325,11 +3888,15 @@ int ath12k_qmi_init_service(struct ath12k_base *ab) void ath12k_qmi_deinit_service(struct ath12k_base *ab) { + if (!ab->qmi.ab) + return; + qmi_handle_release(&ab->qmi.handle); cancel_work_sync(&ab->qmi.event_work); destroy_workqueue(ab->qmi.event_wq); ath12k_qmi_m3_free(ab); ath12k_qmi_free_target_mem_chunk(ab); + ab->qmi.ab = NULL; } void ath12k_qmi_free_resource(struct ath12k_base *ab) diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index 6ee33c9851c6..96e6c3daecfe 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_QMI_H @@ -21,6 +21,7 @@ #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1 #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9274 0x07 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332 0x2 #define ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 #define ATH12K_QMI_RESP_LEN_MAX 8192 #define ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 @@ -41,6 +42,7 @@ #define ATH12K_BOARD_ID_DEFAULT 0xFF struct ath12k_base; +struct ath12k_hw_group; enum ath12k_qmi_file_type { ATH12K_QMI_FILE_TYPE_BDF_GOLDEN = 0, @@ -68,6 +70,7 @@ enum ath12k_qmi_event_type { ATH12K_QMI_EVENT_FORCE_FW_ASSERT, ATH12K_QMI_EVENT_POWER_UP, ATH12K_QMI_EVENT_POWER_DOWN, + ATH12K_QMI_EVENT_HOST_CAP, ATH12K_QMI_EVENT_MAX, }; @@ -96,6 +99,8 @@ struct ath12k_qmi_event_msg { struct target_mem_chunk { u32 size; u32 type; + u32 prev_size; + u32 prev_type; dma_addr_t paddr; union { void __iomem *ioaddr; @@ -140,6 +145,10 @@ struct ath12k_qmi { u32 target_mem_mode; bool target_mem_delayed; u8 cal_done; + + /* protected with struct ath12k_qmi::event_lock */ + bool block_event; + u8 num_radios; struct target_info target; struct m3_mem_region m3_mem; @@ -165,6 +174,7 @@ enum ath12k_qmi_target_mem { BDF_MEM_REGION_TYPE = 0x2, M3_DUMP_REGION_TYPE = 0x3, CALDB_MEM_REGION_TYPE = 0x4, + MLO_GLOBAL_MEM_REGION_TYPE = 0x8, PAGEABLE_MEM_REGION_TYPE = 0x9, }; @@ -265,6 +275,8 @@ struct qmi_wlanfw_phy_cap_resp_msg_v01 { u8 num_phy; u8 board_id_valid; u32 board_id; + u8 single_chip_mlo_support_valid; + u8 single_chip_mlo_support; }; #define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 @@ -590,11 +602,27 @@ struct qmi_wlanfw_wlan_ini_resp_msg_v01 { struct qmi_response_type_v01 resp; }; +static inline void ath12k_qmi_set_event_block(struct ath12k_qmi *qmi, bool block) +{ + lockdep_assert_held(&qmi->event_lock); + + qmi->block_event = block; +} + +static inline bool ath12k_qmi_get_event_block(struct ath12k_qmi *qmi) +{ + lockdep_assert_held(&qmi->event_lock); + + return qmi->block_event; +} + int ath12k_qmi_firmware_start(struct ath12k_base *ab, u32 mode); void ath12k_qmi_firmware_stop(struct ath12k_base *ab); void ath12k_qmi_deinit_service(struct ath12k_base *ab); int ath12k_qmi_init_service(struct ath12k_base *ab); void ath12k_qmi_free_resource(struct ath12k_base *ab); +void ath12k_qmi_trigger_host_cap(struct ath12k_base *ab); +void ath12k_qmi_reset_mlo_mem(struct ath12k_hw_group *ag); #endif diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index f308e9a6ed55..2598b39d5d7e 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/rtnetlink.h> #include "core.h" #include "debug.h" +#include "mac.h" /* World regdom to be used in case default regd from fw is unavailable */ #define ATH12K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0) @@ -48,13 +49,32 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath12k_wmi_init_country_arg arg; + struct wmi_set_current_country_arg current_arg = {}; struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar = ath12k_ah_to_ar(ah); - int ret; + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + int ret, i; ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "driver initiated regd update\n"); + if (ah->state != ATH12K_HW_STATE_ON) + return; + + for_each_ar(ah, ar, i) { + ret = ath12k_reg_update_chan_list(ar, true); + if (ret) { + ath12k_warn(ar->ab, + "failed to update chan list for pdev %u, ret %d\n", + i, ret); + break; + } + } + return; + } + /* Currently supporting only General User Hints. Cell base user * hints to be handled later. * Hints from other sources like Core, Beacons are not expected for @@ -77,21 +97,38 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) return; } - /* Set the country code to the firmware and wait for - * the WMI_REG_CHAN_LIST_CC EVENT for updating the - * reg info - */ - arg.flags = ALPHA_IS_SET; - memcpy(&arg.cc_info.alpha2, request->alpha2, 2); - arg.cc_info.alpha2[2] = 0; + /* Allow fresh updates to wiphy regd */ + ah->regd_updated = false; + + /* Send the reg change request to all the radios */ + for_each_ar(ah, ar, i) { + if (ar->ab->hw_params->current_cc_support) { + memcpy(¤t_arg.alpha2, request->alpha2, 2); + memcpy(&ar->alpha2, ¤t_arg.alpha2, 2); + ret = ath12k_wmi_send_set_current_country_cmd(ar, ¤t_arg); + if (ret) + ath12k_warn(ar->ab, + "failed set current country code: %d\n", ret); + } else { + arg.flags = ALPHA_IS_SET; + memcpy(&arg.cc_info.alpha2, request->alpha2, 2); + arg.cc_info.alpha2[2] = 0; + + ret = ath12k_wmi_send_init_country_cmd(ar, &arg); + if (ret) + ath12k_warn(ar->ab, + "failed set INIT Country code: %d\n", ret); + } - ret = ath12k_wmi_send_init_country_cmd(ar, &arg); - if (ret) - ath12k_warn(ar->ab, - "INIT Country code set to fw failed : %d\n", ret); + wiphy_lock(wiphy); + ath12k_mac_11d_scan_stop(ar); + wiphy_unlock(wiphy); + + ar->regdom_set_by_user = true; + } } -int ath12k_reg_update_chan_list(struct ath12k *ar) +int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait) { struct ieee80211_supported_band **bands; struct ath12k_wmi_scan_chan_list_arg *arg; @@ -100,7 +137,35 @@ int ath12k_reg_update_chan_list(struct ath12k *ar) struct ath12k_wmi_channel_arg *ch; enum nl80211_band band; int num_channels = 0; - int i, ret; + int i, ret, left; + + if (wait && ar->state_11d == ATH12K_11D_RUNNING) { + left = wait_for_completion_timeout(&ar->completed_11d_scan, + ATH12K_SCAN_TIMEOUT_HZ); + if (!left) { + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "failed to receive 11d scan complete: timed out\n"); + ar->state_11d = ATH12K_11D_IDLE; + } + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "reg 11d scan wait left time %d\n", left); + } + + if (wait && + (ar->scan.state == ATH12K_SCAN_STARTING || + ar->scan.state == ATH12K_SCAN_RUNNING)) { + left = wait_for_completion_timeout(&ar->scan.completed, + ATH12K_SCAN_TIMEOUT_HZ); + if (!left) + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "failed to receive hw scan complete: timed out\n"); + + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "reg hw scan wait left time %d\n", left); + } + + if (ar->ah->state == ATH12K_HW_STATE_RESTARTING) + return 0; bands = hw->wiphy->bands; for (band = 0; band < NUM_NL80211_BANDS; band++) { @@ -200,12 +265,76 @@ static void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig, int ath12k_regd_update(struct ath12k *ar, bool init) { - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; + u32 phy_id, freq_low, freq_high, supported_bands; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + struct ieee80211_hw *hw = ah->hw; struct ieee80211_regdomain *regd, *regd_copy = NULL; int ret, regd_len, pdev_id; struct ath12k_base *ab; ab = ar->ab; + + supported_bands = ar->pdev->cap.supported_bands; + reg_cap = &ab->hal_reg_cap[ar->pdev_idx]; + + /* Possible that due to reg change, current limits for supported + * frequency changed. Update it. As a first step, reset the + * previous values and then compute and set the new values. + */ + ar->freq_range.start_freq = 0; + ar->freq_range.end_freq = 0; + + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { + if (ab->hw_params->single_pdev_only) { + phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_2GHZ_CAP].phy_id; + reg_cap = &ab->hal_reg_cap[phy_id]; + } + + freq_low = max(reg_cap->low_2ghz_chan, ab->reg_freq_2ghz.start_freq); + freq_high = min(reg_cap->high_2ghz_chan, ab->reg_freq_2ghz.end_freq); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + } + + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) { + if (ab->hw_params->single_pdev_only) { + phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_5GHZ_CAP].phy_id; + reg_cap = &ab->hal_reg_cap[phy_id]; + } + + freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_5ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_5ghz.end_freq); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + } + + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { + freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_6ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_6ghz.end_freq); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + } + + /* If one of the radios within ah has already updated the regd for + * the wiphy, then avoid setting regd again + */ + if (ah->regd_updated) + return 0; + + /* firmware provides reg rules which are similar for 2 GHz and 5 GHz + * pdev but 6 GHz pdev has superset of all rules including rules for + * all bands, we prefer 6 GHz pdev's rules to be used for setup of + * the wiphy regd. + * If 6 GHz pdev was part of the ath12k_hw, wait for the 6 GHz pdev, + * else pick the first pdev which calls this function and use its + * regd to update global hw regd. + * The regd_updated flag set at the end will not allow any further + * updates. + */ + if (ah->use_6ghz_regd && !ar->supports_6ghz) + return 0; + pdev_id = ar->pdev_idx; spin_lock_bh(&ab->base_lock); @@ -247,23 +376,19 @@ int ath12k_regd_update(struct ath12k *ar, bool init) goto err; } - rtnl_lock(); - wiphy_lock(hw->wiphy); - ret = regulatory_set_wiphy_regd_sync(hw->wiphy, regd_copy); - wiphy_unlock(hw->wiphy); - rtnl_unlock(); + ret = regulatory_set_wiphy_regd(hw->wiphy, regd_copy); kfree(regd_copy); if (ret) goto err; - if (ar->state == ATH12K_STATE_ON) { - ret = ath12k_reg_update_chan_list(ar); - if (ret) - goto err; - } + if (ah->state != ATH12K_HW_STATE_ON) + goto skip; + ah->regd_updated = true; + +skip: return 0; err: ath12k_warn(ab, "failed to perform regd update : %d\n", ret); @@ -329,129 +454,6 @@ static u32 ath12k_map_fw_phy_flags(u32 phy_flags) return flags; } -static bool -ath12k_reg_can_intersect(struct ieee80211_reg_rule *rule1, - struct ieee80211_reg_rule *rule2) -{ - u32 start_freq1, end_freq1; - u32 start_freq2, end_freq2; - - start_freq1 = rule1->freq_range.start_freq_khz; - start_freq2 = rule2->freq_range.start_freq_khz; - - end_freq1 = rule1->freq_range.end_freq_khz; - end_freq2 = rule2->freq_range.end_freq_khz; - - if ((start_freq1 >= start_freq2 && - start_freq1 < end_freq2) || - (start_freq2 > start_freq1 && - start_freq2 < end_freq1)) - return true; - - /* TODO: Should we restrict intersection feasibility - * based on min bandwidth of the intersected region also, - * say the intersected rule should have a min bandwidth - * of 20MHz? - */ - - return false; -} - -static void ath12k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, - struct ieee80211_reg_rule *rule2, - struct ieee80211_reg_rule *new_rule) -{ - u32 start_freq1, end_freq1; - u32 start_freq2, end_freq2; - u32 freq_diff, max_bw; - - start_freq1 = rule1->freq_range.start_freq_khz; - start_freq2 = rule2->freq_range.start_freq_khz; - - end_freq1 = rule1->freq_range.end_freq_khz; - end_freq2 = rule2->freq_range.end_freq_khz; - - new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1, - start_freq2); - new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2); - - freq_diff = new_rule->freq_range.end_freq_khz - - new_rule->freq_range.start_freq_khz; - max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz, - rule2->freq_range.max_bandwidth_khz); - new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff); - - new_rule->power_rule.max_antenna_gain = - min_t(u32, rule1->power_rule.max_antenna_gain, - rule2->power_rule.max_antenna_gain); - - new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp, - rule2->power_rule.max_eirp); - - /* Use the flags of both the rules */ - new_rule->flags = rule1->flags | rule2->flags; - - /* To be safe, lts use the max cac timeout of both rules */ - new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, - rule2->dfs_cac_ms); -} - -static struct ieee80211_regdomain * -ath12k_regd_intersect(struct ieee80211_regdomain *default_regd, - struct ieee80211_regdomain *curr_regd) -{ - u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules; - struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule; - struct ieee80211_regdomain *new_regd = NULL; - u8 i, j, k; - - num_old_regd_rules = default_regd->n_reg_rules; - num_curr_regd_rules = curr_regd->n_reg_rules; - num_new_regd_rules = 0; - - /* Find the number of intersecting rules to allocate new regd memory */ - for (i = 0; i < num_old_regd_rules; i++) { - old_rule = default_regd->reg_rules + i; - for (j = 0; j < num_curr_regd_rules; j++) { - curr_rule = curr_regd->reg_rules + j; - - if (ath12k_reg_can_intersect(old_rule, curr_rule)) - num_new_regd_rules++; - } - } - - if (!num_new_regd_rules) - return NULL; - - new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules * - sizeof(struct ieee80211_reg_rule)), - GFP_ATOMIC); - - if (!new_regd) - return NULL; - - /* We set the new country and dfs region directly and only trim - * the freq, power, antenna gain by intersecting with the - * default regdomain. Also MAX of the dfs cac timeout is selected. - */ - new_regd->n_reg_rules = num_new_regd_rules; - memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2)); - new_regd->dfs_region = curr_regd->dfs_region; - new_rule = new_regd->reg_rules; - - for (i = 0, k = 0; i < num_old_regd_rules; i++) { - old_rule = default_regd->reg_rules + i; - for (j = 0; j < num_curr_regd_rules; j++) { - curr_rule = curr_regd->reg_rules + j; - - if (ath12k_reg_can_intersect(old_rule, curr_rule)) - ath12k_reg_intersect_rules(old_rule, curr_rule, - (new_rule + k++)); - } - } - return new_regd; -} - static const char * ath12k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region) { @@ -488,13 +490,14 @@ ath12k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) static void ath12k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, - u32 reg_flags) + s8 psd, u32 reg_flags) { reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); + reg_rule->psd = psd; reg_rule->flags = reg_flags; } @@ -516,7 +519,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, ath12k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -538,7 +541,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; @@ -563,7 +566,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -575,26 +578,77 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, *rule_idx = i; } +static void ath12k_reg_update_freq_range(struct ath12k_reg_freq *reg_freq, + struct ath12k_reg_rule *reg_rule) +{ + if (reg_freq->start_freq > reg_rule->start_freq) + reg_freq->start_freq = reg_rule->start_freq; + + if (reg_freq->end_freq < reg_rule->end_freq) + reg_freq->end_freq = reg_rule->end_freq; +} + +enum wmi_reg_6g_ap_type +ath12k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type) +{ + switch (power_type) { + case IEEE80211_REG_LPI_AP: + return WMI_REG_INDOOR_AP; + case IEEE80211_REG_SP_AP: + return WMI_REG_STD_POWER_AP; + case IEEE80211_REG_VLP_AP: + return WMI_REG_VLP_AP; + default: + return WMI_REG_MAX_AP_TYPE; + } +} + struct ieee80211_regdomain * ath12k_reg_build_regd(struct ath12k_base *ab, - struct ath12k_reg_info *reg_info, bool intersect) + struct ath12k_reg_info *reg_info, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type) { - struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; - struct ath12k_reg_rule *reg_rule; + struct ieee80211_regdomain *new_regd = NULL; + struct ath12k_reg_rule *reg_rule, *reg_rule_6ghz; + u32 flags, reg_6ghz_number, max_bw_6ghz; u8 i = 0, j = 0, k = 0; u8 num_rules; u16 max_bw; - u32 flags; char alpha2[3]; num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; - /* FIXME: Currently taking reg rules for 6G only from Indoor AP mode list. - * This can be updated to choose the combination dynamically based on AP - * type and client type, after complete 6G regulatory support is added. - */ - if (reg_info->is_ext_reg_event) - num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP]; + if (reg_info->is_ext_reg_event) { + if (vdev_type == WMI_VDEV_TYPE_STA) { + enum wmi_reg_6g_ap_type ap_type; + + ap_type = ath12k_reg_ap_pwr_convert(power_type); + if (ap_type == WMI_REG_MAX_AP_TYPE) + ap_type = WMI_REG_INDOOR_AP; + + reg_6ghz_number = reg_info->num_6g_reg_rules_cl + [ap_type][WMI_REG_DEFAULT_CLIENT]; + if (reg_6ghz_number == 0) { + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6g_reg_rules_cl + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } + + reg_rule_6ghz = reg_info->reg_rules_6g_client_ptr + [ap_type][WMI_REG_DEFAULT_CLIENT]; + max_bw_6ghz = reg_info->max_bw_6g_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } else { + reg_6ghz_number = reg_info->num_6g_reg_rules_ap + [WMI_REG_INDOOR_AP]; + reg_rule_6ghz = + reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP]; + max_bw_6ghz = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]; + } + + num_rules += reg_6ghz_number; + } if (!num_rules) goto ret; @@ -603,21 +657,31 @@ ath12k_reg_build_regd(struct ath12k_base *ab, if (reg_info->dfs_region == ATH12K_DFS_REG_ETSI) num_rules += 2; - tmp_regd = kzalloc(sizeof(*tmp_regd) + + new_regd = kzalloc(sizeof(*new_regd) + (num_rules * sizeof(struct ieee80211_reg_rule)), GFP_ATOMIC); - if (!tmp_regd) + if (!new_regd) goto ret; - memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); + memcpy(new_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); alpha2[2] = '\0'; - tmp_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region); + new_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region); ath12k_dbg(ab, ATH12K_DBG_REG, "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", - alpha2, ath12k_reg_get_regdom_str(tmp_regd->dfs_region), + alpha2, ath12k_reg_get_regdom_str(new_regd->dfs_region), reg_info->dfs_region, num_rules); + + /* Reset start and end frequency for each band + */ + ab->reg_freq_5ghz.start_freq = INT_MAX; + ab->reg_freq_5ghz.end_freq = 0; + ab->reg_freq_2ghz.start_freq = INT_MAX; + ab->reg_freq_2ghz.end_freq = 0; + ab->reg_freq_6ghz.start_freq = INT_MAX; + ab->reg_freq_6ghz.end_freq = 0; + /* Update reg_rules[] below. Firmware is expected to * send these rules in order(2G rules first and then 5G) */ @@ -628,6 +692,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, max_bw = min_t(u16, reg_rule->max_bw, reg_info->max_bw_2g); flags = 0; + ath12k_reg_update_freq_range(&ab->reg_freq_2ghz, reg_rule); } else if (reg_info->num_5g_reg_rules && (j < reg_info->num_5g_reg_rules)) { reg_rule = reg_info->reg_rules_5g_ptr + j++; @@ -641,13 +706,15 @@ ath12k_reg_build_regd(struct ath12k_base *ab, * per other BW rule flags we pass from here */ flags = NL80211_RRF_AUTO_BW; - } else if (reg_info->is_ext_reg_event && - reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] && - (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) { - reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++; - max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]); + ath12k_reg_update_freq_range(&ab->reg_freq_5ghz, reg_rule); + } else if (reg_info->is_ext_reg_event && reg_6ghz_number && + (k < reg_6ghz_number)) { + reg_rule = reg_rule_6ghz + k++; + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); flags = NL80211_RRF_AUTO_BW; + if (reg_rule->psd_flag) + flags |= NL80211_RRF_PSD; + ath12k_reg_update_freq_range(&ab->reg_freq_6ghz, reg_rule); } else { break; } @@ -655,11 +722,11 @@ ath12k_reg_build_regd(struct ath12k_base *ab, flags |= ath12k_map_fw_reg_flags(reg_rule->flags); flags |= ath12k_map_fw_phy_flags(reg_info->phybitmap); - ath12k_reg_update_rule(tmp_regd->reg_rules + i, + ath12k_reg_update_rule(new_regd->reg_rules + i, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); /* Update dfs cac timeout if the dfs domain is ETSI and the * new rule covers weather radar band. @@ -670,7 +737,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, reg_info->dfs_region == ATH12K_DFS_REG_ETSI && (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW && reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){ - ath12k_reg_update_weather_radar_band(ab, tmp_regd, + ath12k_reg_update_weather_radar_band(ab, new_regd, reg_rule, &i, flags, max_bw); continue; @@ -680,36 +747,19 @@ ath12k_reg_build_regd(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n", i + 1, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - tmp_regd->reg_rules[i].dfs_cac_ms, + new_regd->reg_rules[i].dfs_cac_ms, flags, reg_rule->psd_flag, reg_rule->psd_eirp); } else { ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", i + 1, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - tmp_regd->reg_rules[i].dfs_cac_ms, + new_regd->reg_rules[i].dfs_cac_ms, flags); } } - tmp_regd->n_reg_rules = i; - - if (intersect) { - default_regd = ab->default_regd[reg_info->phy_id]; - - /* Get a new regd by intersecting the received regd with - * our default regd. - */ - new_regd = ath12k_regd_intersect(default_regd, tmp_regd); - kfree(tmp_regd); - if (!new_regd) { - ath12k_warn(ab, "Unable to create intersected regdomain\n"); - goto ret; - } - } else { - new_regd = tmp_regd; - } - + new_regd->n_reg_rules = i; ret: return new_regd; } @@ -731,9 +781,109 @@ void ath12k_regd_update_work(struct work_struct *work) } } +void ath12k_reg_reset_reg_info(struct ath12k_reg_info *reg_info) +{ + u8 i, j; + + if (!reg_info) + return; + + kfree(reg_info->reg_rules_2g_ptr); + kfree(reg_info->reg_rules_5g_ptr); + + if (reg_info->is_ext_reg_event) { + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + kfree(reg_info->reg_rules_6g_ap_ptr[i]); + + for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++) + kfree(reg_info->reg_rules_6g_client_ptr[i][j]); + } + } +} + +enum ath12k_reg_status ath12k_reg_validate_reg_info(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info) +{ + int pdev_idx = reg_info->phy_id; + + if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { + /* In case of failure to set the requested country, + * firmware retains the current regd. We print a failure info + * and return from here. + */ + ath12k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + return ATH12K_REG_STATUS_DROP; + } + + if (pdev_idx >= ab->num_radios) { + /* Process the event for phy0 only if single_pdev_only + * is true. If pdev_idx is valid but not 0, discard the + * event. Otherwise, it goes to fallback. + */ + if (ab->hw_params->single_pdev_only && + pdev_idx < ab->hw_params->num_rxdma_per_pdev) + return ATH12K_REG_STATUS_DROP; + else + return ATH12K_REG_STATUS_FALLBACK; + } + + /* Avoid multiple overwrites to default regd, during core + * stop-start after mac registration. + */ + if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && + !memcmp(ab->default_regd[pdev_idx]->alpha2, + reg_info->alpha2, 2)) + return ATH12K_REG_STATUS_DROP; + + return ATH12K_REG_STATUS_VALID; +} + +int ath12k_reg_handle_chan_list(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type) +{ + struct ieee80211_regdomain *regd = NULL; + int pdev_idx = reg_info->phy_id; + struct ath12k *ar; + + regd = ath12k_reg_build_regd(ab, reg_info, vdev_type, power_type); + if (!regd) + return -EINVAL; + + spin_lock_bh(&ab->base_lock); + if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { + /* Once mac is registered, ar is valid and all CC events from + * firmware is considered to be received due to user requests + * currently. + * Free previously built regd before assigning the newly + * generated regd to ar. NULL pointer handling will be + * taken care by kfree itself. + */ + ar = ab->pdevs[pdev_idx].ar; + kfree(ab->new_regd[pdev_idx]); + ab->new_regd[pdev_idx] = regd; + queue_work(ab->workqueue, &ar->regd_update_work); + } else { + /* Multiple events for the same *ar is not expected. But we + * can still clear any previously stored default_regd if we + * are receiving this event for the same radio by mistake. + * NULL pointer handling will be taken care by kfree itself. + */ + kfree(ab->default_regd[pdev_idx]); + /* This regd would be applied during mac registration */ + ab->default_regd[pdev_idx] = regd; + } + ab->dfs_region = reg_info->dfs_region; + spin_unlock_bh(&ab->base_lock); + + return 0; +} + void ath12k_reg_init(struct ieee80211_hw *hw) { hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER; hw->wiphy->reg_notifier = ath12k_reg_notifier; } @@ -741,8 +891,18 @@ void ath12k_reg_free(struct ath12k_base *ab) { int i; + mutex_lock(&ab->core_lock); + for (i = 0; i < MAX_RADIOS; i++) { + ath12k_reg_reset_reg_info(ab->reg_info[i]); + kfree(ab->reg_info[i]); + ab->reg_info[i] = NULL; + } + for (i = 0; i < ab->hw_params->max_radios; i++) { kfree(ab->default_regd[i]); kfree(ab->new_regd[i]); + ab->default_regd[i] = NULL; + ab->new_regd[i] = NULL; } + mutex_unlock(&ab->core_lock); } diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index 29c7ec3260da..8af8e9ba462e 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_REG_H @@ -13,6 +13,9 @@ struct ath12k_base; struct ath12k; +#define ATH12K_2GHZ_MAX_FREQUENCY 2495 +#define ATH12K_5GHZ_MAX_FREQUENCY 5920 + /* DFS regdomains supported by Firmware */ enum ath12k_dfs_region { ATH12K_DFS_REG_UNSET, @@ -89,13 +92,29 @@ enum ath12k_reg_phy_bitmap { ATH12K_REG_PHY_BITMAP_NO11BE = BIT(6), }; +enum ath12k_reg_status { + ATH12K_REG_STATUS_VALID, + ATH12K_REG_STATUS_DROP, + ATH12K_REG_STATUS_FALLBACK, +}; + void ath12k_reg_init(struct ieee80211_hw *hw); void ath12k_reg_free(struct ath12k_base *ab); void ath12k_regd_update_work(struct work_struct *work); struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab, struct ath12k_reg_info *reg_info, - bool intersect); + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); int ath12k_regd_update(struct ath12k *ar, bool init); -int ath12k_reg_update_chan_list(struct ath12k *ar); +int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait); +void ath12k_reg_reset_reg_info(struct ath12k_reg_info *reg_info); +int ath12k_reg_handle_chan_list(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); +enum wmi_reg_6g_ap_type +ath12k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type); +enum ath12k_reg_status ath12k_reg_validate_reg_info(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info); #endif diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index a0db6702a189..6c600473b402 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_RX_DESC_H #define ATH12K_RX_DESC_H @@ -637,6 +637,8 @@ enum rx_msdu_start_pkt_type { RX_MSDU_START_PKT_TYPE_11N, RX_MSDU_START_PKT_TYPE_11AC, RX_MSDU_START_PKT_TYPE_11AX, + RX_MSDU_START_PKT_TYPE_11BA, + RX_MSDU_START_PKT_TYPE_11BE, }; enum rx_msdu_start_sgi { @@ -684,18 +686,17 @@ enum rx_msdu_start_reception_type { #define RX_MSDU_END_INFO5_SA_IDX_TIMEOUT BIT(0) #define RX_MSDU_END_INFO5_DA_IDX_TIMEOUT BIT(1) +#define RX_MSDU_END_INFO5_TO_DS BIT(2) +#define RX_MSDU_END_INFO5_TID GENMASK(6, 3) #define RX_MSDU_END_INFO5_SA_IS_VALID BIT(7) #define RX_MSDU_END_INFO5_DA_IS_VALID BIT(8) #define RX_MSDU_END_INFO5_DA_IS_MCBC BIT(9) #define RX_MSDU_END_INFO5_L3_HDR_PADDING GENMASK(11, 10) #define RX_MSDU_END_INFO5_FIRST_MSDU BIT(12) #define RX_MSDU_END_INFO5_LAST_MSDU BIT(13) +#define RX_MSDU_END_INFO5_FROM_DS BIT(14) #define RX_MSDU_END_INFO5_IP_CHKSUM_FAIL_COPY BIT(15) -#define RX_MSDU_END_QCN9274_INFO5_TO_DS BIT(2) -#define RX_MSDU_END_QCN9274_INFO5_TID GENMASK(6, 3) -#define RX_MSDU_END_QCN9274_INFO5_FROM_DS BIT(14) - #define RX_MSDU_END_INFO6_MSDU_DROP BIT(0) #define RX_MSDU_END_INFO6_REO_DEST_IND GENMASK(5, 1) #define RX_MSDU_END_INFO6_FLOW_IDX GENMASK(25, 6) @@ -709,14 +710,14 @@ enum rx_msdu_start_reception_type { #define RX_MSDU_END_INFO7_FLOW_AGGR_CONTN BIT(8) #define RX_MSDU_END_INFO7_FISA_TIMEOUT BIT(9) -#define RX_MSDU_END_QCN9274_INFO7_TCPUDP_CSUM_FAIL_CPY BIT(10) -#define RX_MSDU_END_QCN9274_INFO7_MSDU_LIMIT_ERROR BIT(11) -#define RX_MSDU_END_QCN9274_INFO7_FLOW_IDX_TIMEOUT BIT(12) -#define RX_MSDU_END_QCN9274_INFO7_FLOW_IDX_INVALID BIT(13) -#define RX_MSDU_END_QCN9274_INFO7_CCE_MATCH BIT(14) -#define RX_MSDU_END_QCN9274_INFO7_AMSDU_PARSER_ERR BIT(15) +#define RX_MSDU_END_INFO7_TCPUDP_CSUM_FAIL_CPY BIT(10) +#define RX_MSDU_END_INFO7_MSDU_LIMIT_ERROR BIT(11) +#define RX_MSDU_END_INFO7_FLOW_IDX_TIMEOUT BIT(12) +#define RX_MSDU_END_INFO7_FLOW_IDX_INVALID BIT(13) +#define RX_MSDU_END_INFO7_CCE_MATCH BIT(14) +#define RX_MSDU_END_INFO7_AMSDU_PARSER_ERR BIT(15) -#define RX_MSDU_END_QCN9274_INFO8_KEY_ID GENMASK(7, 0) +#define RX_MSDU_END_INFO8_KEY_ID GENMASK(7, 0) #define RX_MSDU_END_INFO9_SERVICE_CODE GENMASK(14, 6) #define RX_MSDU_END_INFO9_PRIORITY_VALID BIT(15) @@ -758,8 +759,8 @@ enum rx_msdu_start_reception_type { #define RX_MSDU_END_INFO12_RECV_BW GENMASK(20, 18) #define RX_MSDU_END_INFO12_RECEPTION_TYPE GENMASK(23, 21) -#define RX_MSDU_END_QCN9274_INFO12_MIMO_SS_BITMAP GENMASK(30, 24) -#define RX_MSDU_END_QCN9274_INFO12_MIMO_DONE_COPY BIT(31) +#define RX_MSDU_END_INFO12_MIMO_SS_BITMAP GENMASK(30, 24) +#define RX_MSDU_END_INFO12_MIMO_DONE_COPY BIT(31) #define RX_MSDU_END_INFO13_FIRST_MPDU BIT(0) #define RX_MSDU_END_INFO13_MCAST_BCAST BIT(2) @@ -791,7 +792,7 @@ enum rx_msdu_start_reception_type { #define RX_MSDU_END_INFO13_UNDECRYPT_FRAME_ERR BIT(30) #define RX_MSDU_END_INFO13_FCS_ERR BIT(31) -#define RX_MSDU_END_QCN9274_INFO13_WIFI_PARSER_ERR BIT(15) +#define RX_MSDU_END_INFO13_WIFI_PARSER_ERR BIT(15) #define RX_MSDU_END_INFO14_DECRYPT_STATUS_CODE GENMASK(12, 10) #define RX_MSDU_END_INFO14_RX_BITMAP_NOT_UPDED BIT(13) @@ -889,65 +890,6 @@ struct rx_msdu_end_qcn9274_compact { __le32 info14; } __packed; -/* These macro definitions are only used for WCN7850 */ -#define RX_MSDU_END_WCN7850_INFO2_KEY_ID BIT(7, 0) - -#define RX_MSDU_END_WCN7850_INFO5_MSDU_LIMIT_ERR BIT(2) -#define RX_MSDU_END_WCN7850_INFO5_IDX_TIMEOUT BIT(3) -#define RX_MSDU_END_WCN7850_INFO5_IDX_INVALID BIT(4) -#define RX_MSDU_END_WCN7850_INFO5_WIFI_PARSE_ERR BIT(5) -#define RX_MSDU_END_WCN7850_INFO5_AMSDU_PARSER_ERR BIT(6) -#define RX_MSDU_END_WCN7850_INFO5_TCPUDP_CSUM_FAIL_CPY BIT(14) - -#define RX_MSDU_END_WCN7850_INFO12_MIMO_SS_BITMAP GENMASK(31, 24) - -#define RX_MSDU_END_WCN7850_INFO13_FRAGMENT_FLAG BIT(13) -#define RX_MSDU_END_WCN7850_INFO13_CCE_MATCH BIT(15) - -struct rx_msdu_end_wcn7850 { - __le16 info0; - __le16 phy_ppdu_id; - __le16 ip_hdr_cksum; - __le16 info1; - __le16 info2; - __le16 cumulative_l3_checksum; - __le32 rule_indication0; - __le32 rule_indication1; - __le16 info3; - __le16 l3_type; - __le32 ipv6_options_crc; - __le32 tcp_seq_num; - __le32 tcp_ack_num; - __le16 info4; - __le16 window_size; - __le16 tcp_udp_chksum; - __le16 info5; - __le16 sa_idx; - __le16 da_idx_or_sw_peer_id; - __le32 info6; - __le32 fse_metadata; - __le16 cce_metadata; - __le16 sa_sw_peer_id; - __le16 info7; - __le16 rsvd0; - __le16 cumulative_l4_checksum; - __le16 cumulative_ip_length; - __le32 info9; - __le32 info10; - __le32 info11; - __le32 toeplitz_hash_2_or_4; - __le32 flow_id_toeplitz; - __le32 info12; - __le32 ppdu_start_timestamp_31_0; - __le32 ppdu_start_timestamp_63_32; - __le32 phy_meta_data; - __le16 vlan_ctag_ci; - __le16 vlan_stag_ci; - __le32 rsvd[3]; - __le32 info13; - __le32 info14; -} __packed; - /* rx_msdu_end * * rxpcu_mpdu_filter_in_category @@ -1578,7 +1520,7 @@ struct rx_pkt_hdr_tlv { struct hal_rx_desc_wcn7850 { __le64 msdu_end_tag; - struct rx_msdu_end_wcn7850 msdu_end; + struct rx_msdu_end_qcn9274 msdu_end; u8 rx_padding0[RX_BE_PADDING0_BYTES]; __le64 mpdu_start_tag; struct rx_mpdu_start_qcn9274 mpdu_start; @@ -1599,12 +1541,4 @@ struct hal_rx_desc { #define MAX_MU_GROUP_SHOW 16 #define MAX_MU_GROUP_LENGTH (6 * MAX_MU_GROUP_SHOW) -#define HAL_RX_RU_ALLOC_TYPE_MAX 6 -#define RU_26 1 -#define RU_52 2 -#define RU_106 4 -#define RU_242 9 -#define RU_484 18 -#define RU_996 37 - #endif /* ATH12K_RX_DESC_H */ diff --git a/drivers/net/wireless/ath/ath12k/testmode.c b/drivers/net/wireless/ath/ath12k/testmode.c new file mode 100644 index 000000000000..fb6af7ccf71f --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/testmode.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "testmode.h" +#include <net/netlink.h> +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "core.h" +#include "hif.h" +#include "../testmode_i.h" + +#define ATH12K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) +#define ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) + +static const struct nla_policy ath12k_tm_policy[ATH_TM_ATTR_MAX + 1] = { + [ATH_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH_TM_DATA_MAX_LEN }, + [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +}; + +static struct ath12k *ath12k_tm_get_ar(struct ath12k_base *ab) +{ + struct ath12k_pdev *pdev; + struct ath12k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + if (ar && ar->ah->state == ATH12K_HW_STATE_TM) + return ar; + } + + return NULL; +} + +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + struct sk_buff *nl_skb; + struct ath12k *ar; + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "testmode event wmi cmd_id %d skb length %d\n", + cmd_id, skb->len); + + ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", skb->data, skb->len); + + ar = ath12k_tm_get_ar(ab); + if (!ar) { + ath12k_warn(ab, "testmode event not handled due to invalid pdev\n"); + return; + } + + spin_lock_bh(&ar->data_lock); + + nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(skb->len), + GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + + if (!nl_skb) { + ath12k_warn(ab, + "failed to allocate skb for unsegmented testmode wmi event\n"); + return; + } + + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) { + ath12k_warn(ab, "failed to populate testmode unsegmented event\n"); + kfree_skb(nl_skb); + return; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); +} + +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *ftm_msg, + u16 length) +{ + struct sk_buff *nl_skb; + struct ath12k *ar; + u32 data_pos, pdev_id; + u16 datalen; + u8 total_segments, current_seq; + u8 const *buf_pos; + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "testmode event wmi cmd_id %d ftm event msg %p datalen %d\n", + cmd_id, ftm_msg, length); + ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", ftm_msg, length); + pdev_id = DP_HW2SW_MACID(le32_to_cpu(ftm_msg->seg_hdr.pdev_id)); + + if (pdev_id >= ab->num_radios) { + ath12k_warn(ab, "testmode event not handled due to invalid pdev id\n"); + return; + } + + ar = ab->pdevs[pdev_id].ar; + + if (!ar) { + ath12k_warn(ab, "testmode event not handled due to absence of pdev\n"); + return; + } + + current_seq = le32_get_bits(ftm_msg->seg_hdr.segmentinfo, + ATH12K_FTM_SEGHDR_CURRENT_SEQ); + total_segments = le32_get_bits(ftm_msg->seg_hdr.segmentinfo, + ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS); + datalen = length - (sizeof(struct ath12k_wmi_ftm_seg_hdr_params)); + buf_pos = ftm_msg->data; + + if (current_seq == 0) { + ab->ftm_event_obj.expected_seq = 0; + ab->ftm_event_obj.data_pos = 0; + } + + data_pos = ab->ftm_event_obj.data_pos; + + if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) { + ath12k_warn(ab, + "Invalid event length date_pos[%d] datalen[%d]\n", + data_pos, datalen); + return; + } + + memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen); + data_pos += datalen; + + if (++ab->ftm_event_obj.expected_seq != total_segments) { + ab->ftm_event_obj.data_pos = data_pos; + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "partial data received current_seq[%d], total_seg[%d]\n", + current_seq, total_segments); + return; + } + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "total data length[%d] = [%d]\n", + data_pos, ftm_msg->seg_hdr.len); + + spin_lock_bh(&ar->data_lock); + nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(data_pos), + GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + + if (!nl_skb) { + ath12k_warn(ab, + "failed to allocate skb for testmode wmi event\n"); + return; + } + + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, + ATH_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos, + &ab->ftm_event_obj.eventdata[0])) { + ath12k_warn(ab, "failed to populate testmode event"); + kfree_skb(nl_skb); + return; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); +} + +static int ath12k_tm_cmd_get_version(struct ath12k *ar, struct nlattr *tb[]) +{ + struct sk_buff *skb; + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd get version_major %d version_minor %d\n", + ATH_TESTMODE_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MINOR); + + spin_lock_bh(&ar->data_lock); + skb = cfg80211_testmode_alloc_reply_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32))); + spin_unlock_bh(&ar->data_lock); + + if (!skb) + return -ENOMEM; + + if (nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MAJOR) || + nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR, + ATH_TESTMODE_VERSION_MINOR)) { + kfree_skb(skb); + return -ENOBUFS; + } + + return cfg80211_testmode_reply(skb); +} + +static int ath12k_tm_cmd_process_ftm(struct ath12k *ar, struct nlattr *tb[]) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct ath12k_wmi_ftm_cmd *ftm_cmd; + int ret = 0; + void *buf; + size_t aligned_len; + u32 cmd_id, buf_len; + u16 chunk_len, total_bytes, num_segments; + u8 segnumber = 0, *bufpos; + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, "ah->state %d\n", ar->ah->state); + if (ar->ah->state != ATH12K_HW_STATE_TM) + return -ENETDOWN; + + if (!tb[ATH_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); + cmd_id = WMI_PDEV_UTF_CMDID; + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf %p buf_len %d\n", + cmd_id, buf, buf_len); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len); + bufpos = buf; + total_bytes = buf_len; + num_segments = total_bytes / MAX_WMI_UTF_LEN; + + if (buf_len - (num_segments * MAX_WMI_UTF_LEN)) + num_segments++; + + while (buf_len) { + if (buf_len > MAX_WMI_UTF_LEN) + chunk_len = MAX_WMI_UTF_LEN; /* MAX message */ + else + chunk_len = buf_len; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len + + sizeof(struct ath12k_wmi_ftm_cmd))); + + if (!skb) + return -ENOMEM; + + ftm_cmd = (struct ath12k_wmi_ftm_cmd *)skb->data; + aligned_len = chunk_len + sizeof(struct ath12k_wmi_ftm_seg_hdr_params); + ftm_cmd->tlv_header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, aligned_len); + ftm_cmd->seg_hdr.len = cpu_to_le32(total_bytes); + ftm_cmd->seg_hdr.msgref = cpu_to_le32(ar->ftm_msgref); + ftm_cmd->seg_hdr.segmentinfo = + le32_encode_bits(num_segments, + ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS) | + le32_encode_bits(segnumber, + ATH12K_FTM_SEGHDR_CURRENT_SEQ); + ftm_cmd->seg_hdr.pdev_id = cpu_to_le32(ar->pdev->pdev_id); + segnumber++; + memcpy(&ftm_cmd->data, bufpos, chunk_len); + ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id); + + if (ret) { + ath12k_warn(ar->ab, "ftm wmi command fail: %d\n", ret); + kfree_skb(skb); + return ret; + } + + buf_len -= chunk_len; + bufpos += chunk_len; + } + + ++ar->ftm_msgref; + return ret; +} + +static int ath12k_tm_cmd_testmode_start(struct ath12k *ar, struct nlattr *tb[]) +{ + if (ar->ah->state == ATH12K_HW_STATE_TM) + return -EALREADY; + + if (ar->ah->state != ATH12K_HW_STATE_OFF) + return -EBUSY; + + ar->ab->ftm_event_obj.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, + GFP_KERNEL); + + if (!ar->ab->ftm_event_obj.eventdata) + return -ENOMEM; + + ar->ah->state = ATH12K_HW_STATE_TM; + ar->ftm_msgref = 0; + return 0; +} + +static int ath12k_tm_cmd_wmi(struct ath12k *ar, struct nlattr *tb[]) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_pdev_set_param_cmd *cmd; + int ret = 0, tag; + void *buf; + u32 cmd_id, buf_len; + + if (!tb[ATH_TM_ATTR_DATA]) + return -EINVAL; + + if (!tb[ATH_TM_ATTR_WMI_CMDID]) + return -EINVAL; + + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); + + if (!buf_len) { + ath12k_warn(ar->ab, "No data present in testmode command\n"); + return -EINVAL; + } + + cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]); + + cmd = buf; + tag = le32_get_bits(cmd->tlv_header, WMI_TLV_TAG); + + if (tag == WMI_TAG_PDEV_SET_PARAM_CMD) + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf length %d\n", + cmd_id, buf_len); + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + + if (!skb) + return -ENOMEM; + + memcpy(skb->data, buf, buf_len); + + ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + dev_kfree_skb(skb); + ath12k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n", + ret); + } + + return ret; +} + +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar = NULL; + struct nlattr *tb[ATH_TM_ATTR_MAX + 1]; + struct ath12k_base *ab; + struct wiphy *wiphy = hw->wiphy; + int ret; + + lockdep_assert_held(&wiphy->mtx); + + ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath12k_tm_policy, + NULL); + if (ret) + return ret; + + if (!tb[ATH_TM_ATTR_CMD]) + return -EINVAL; + + /* TODO: have to handle ar for MLO case */ + if (ah->num_radio) + ar = ah->radio; + + if (!ar) + return -EINVAL; + + ab = ar->ab; + switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) { + case ATH_TM_CMD_WMI: + return ath12k_tm_cmd_wmi(ar, tb); + case ATH_TM_CMD_TESTMODE_START: + return ath12k_tm_cmd_testmode_start(ar, tb); + case ATH_TM_CMD_GET_VERSION: + return ath12k_tm_cmd_get_version(ar, tb); + case ATH_TM_CMD_WMI_FTM: + set_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags); + return ath12k_tm_cmd_process_ftm(ar, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h new file mode 100644 index 000000000000..ef6ab21d19b8 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/testmode.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "core.h" +#include "hif.h" + +#ifdef CONFIG_NL80211_TESTMODE + +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb); +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *ftm_msg, + u16 length); +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); + +#else + +static inline void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ +} + +static inline void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *msg, + u16 length) +{ +} + +static inline int ath12k_tm_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + return 0; +} + +#endif diff --git a/drivers/net/wireless/ath/ath12k/trace.h b/drivers/net/wireless/ath/ath12k/trace.h index 240737e1542d..253c67accb0e 100644 --- a/drivers/net/wireless/ath/ath12k/trace.h +++ b/drivers/net/wireless/ath/ath12k/trace.h @@ -36,8 +36,8 @@ TRACE_EVENT(ath12k_htt_pktlog, ), TP_fast_assign( - __assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->buf_len = buf_len; __entry->pktlog_checksum = pktlog_checksum; memcpy(__get_dynamic_array(pktlog), buf, buf_len); @@ -73,8 +73,8 @@ TRACE_EVENT(ath12k_htt_ppdu_stats, ), TP_fast_assign( - __assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; __entry->info = ar->pdev->timestamp.info; __entry->sync_tstmp_lo_us = ar->pdev->timestamp.sync_timestamp_hi_us; @@ -117,8 +117,8 @@ TRACE_EVENT(ath12k_htt_rxdesc, ), TP_fast_assign( - __assign_str(device, dev_name(ar->ab->dev)); - __assign_str(driver, dev_driver_string(ar->ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; __entry->type = type; __entry->info = ar->pdev->timestamp.info; @@ -153,8 +153,8 @@ TRACE_EVENT(ath12k_wmi_diag, ), TP_fast_assign( - __assign_str(device, dev_name(ab->dev)); - __assign_str(driver, dev_driver_string(ab->dev)); + __assign_str(device); + __assign_str(driver); __entry->len = len; memcpy(__get_dynamic_array(data), data, len); ), diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 9d69a1769926..465f877fc0fb 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -15,16 +15,23 @@ #include <linux/time.h> #include <linux/of.h> #include "core.h" +#include "debugfs.h" #include "debug.h" #include "mac.h" #include "hw.h" #include "peer.h" #include "p2p.h" +#include "testmode.h" struct ath12k_wmi_svc_ready_parse { bool wmi_svc_bitmap_done; }; +struct wmi_tlv_fw_stats_parse { + const struct wmi_stats_event *ev; + struct ath12k_fw_stats *stats; +}; + struct ath12k_wmi_dma_ring_caps_parse { struct ath12k_wmi_dma_ring_caps_params *dma_ring_caps; u32 n_dma_ring_caps; @@ -84,6 +91,11 @@ struct ath12k_wmi_svc_rdy_ext2_parse { bool dma_ring_cap_done; bool spectral_bin_scaling_done; bool mac_phy_caps_ext_done; + bool hal_reg_caps_ext2_done; + bool scan_radio_caps_ext2_done; + bool twt_caps_done; + bool htt_msdu_idx_to_qtype_map_done; + bool dbs_or_sbs_cap_ext_done; }; struct ath12k_wmi_rdy_parse { @@ -171,9 +183,11 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { .min_len = sizeof(struct ath12k_wmi_p2p_noa_info) }, [WMI_TAG_P2P_NOA_EVENT] = { .min_len = sizeof(struct wmi_p2p_noa_event) }, + [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { + .min_len = sizeof(struct wmi_11d_new_cc_event) }, }; -static __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) +__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) { return le32_encode_bits(cmd, WMI_TLV_TAG) | le32_encode_bits(len, WMI_TLV_LEN); @@ -228,9 +242,12 @@ void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, config->peer_map_unmap_version = 0x32; config->twt_ap_pdev_count = ab->num_radios; config->twt_ap_sta_count = 1000; + config->ema_max_vap_cnt = ab->num_radios; + config->ema_max_profile_period = TARGET_EMA_MAX_PROFILE_PERIOD; + config->beacon_tx_offload_max_vdev += config->ema_max_vap_cnt; if (test_bit(WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT, ab->wmi_ab.svc_map)) - config->dp_peer_meta_data_ver = TARGET_RX_PEER_METADATA_VER_V1B; + config->peer_metadata_ver = ATH12K_PEER_METADATA_V1B; } void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -497,6 +514,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, mac_caps = wmi_mac_phy_caps + phy_idx; 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->ampdu_density = le32_to_cpu(mac_caps->ampdu_density); @@ -510,10 +528,10 @@ 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_2G_CAP) { + if (le32_to_cpu(mac_caps->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_5G_CAP) { + } else if (le32_to_cpu(mac_caps->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); @@ -536,7 +554,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_2G_CAP) { + if (le32_to_cpu(mac_caps->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); @@ -556,7 +574,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_5G_CAP) { + if (le32_to_cpu(mac_caps->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 = @@ -810,6 +828,39 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id, return ret; } +int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, + u32 vdev_id, u32 pdev_id) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_request_stats_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_request_stats_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_REQUEST_STATS_CMD, + sizeof(*cmd)); + + cmd->stats_id = cpu_to_le32(stats_id); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pdev_id = cpu_to_le32(pdev_id); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_REQUEST_STATS cmd\n"); + dev_kfree_skb(skb); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "WMI request stats 0x%x vdev id %d pdev id %d\n", + stats_id, vdev_id, pdev_id); + + return ret; +} + int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, struct ath12k_wmi_vdev_create_arg *args) { @@ -817,6 +868,8 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, struct wmi_vdev_create_cmd *cmd; struct sk_buff *skb; struct ath12k_wmi_vdev_txrx_streams_params *txrx_streams; + bool is_ml_vdev = is_valid_ether_addr(args->mld_addr); + struct wmi_vdev_create_mlo_params *ml_params; struct wmi_tlv *tlv; int ret, len; void *ptr; @@ -826,7 +879,8 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, * both the bands. */ len = sizeof(*cmd) + TLV_HDR_SIZE + - (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)); + (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)) + + (is_ml_vdev ? TLV_HDR_SIZE + sizeof(*ml_params) : 0); skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) @@ -841,6 +895,8 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, cmd->vdev_subtype = cpu_to_le32(args->subtype); cmd->num_cfg_txrx_streams = cpu_to_le32(WMI_NUM_SUPPORTED_BAND_MAX); cmd->pdev_id = cpu_to_le32(args->pdev_id); + cmd->mbssid_flags = cpu_to_le32(args->mbssid_flags); + cmd->mbssid_tx_vdev_id = cpu_to_le32(args->mbssid_tx_vdev_id); cmd->vdev_stats_id = cpu_to_le32(args->if_stats_id); ether_addr_copy(cmd->vdev_macaddr.addr, macaddr); @@ -858,20 +914,35 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, len = sizeof(*txrx_streams); txrx_streams->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_TXRX_STREAMS, len); - txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_2G; + txrx_streams->band = cpu_to_le32(WMI_TPC_CHAINMASK_CONFIG_BAND_2G); txrx_streams->supported_tx_streams = - args->chains[NL80211_BAND_2GHZ].tx; + cpu_to_le32(args->chains[NL80211_BAND_2GHZ].tx); txrx_streams->supported_rx_streams = - args->chains[NL80211_BAND_2GHZ].rx; + cpu_to_le32(args->chains[NL80211_BAND_2GHZ].rx); txrx_streams++; txrx_streams->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_TXRX_STREAMS, len); - txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_5G; + txrx_streams->band = cpu_to_le32(WMI_TPC_CHAINMASK_CONFIG_BAND_5G); txrx_streams->supported_tx_streams = - args->chains[NL80211_BAND_5GHZ].tx; + cpu_to_le32(args->chains[NL80211_BAND_5GHZ].tx); txrx_streams->supported_rx_streams = - args->chains[NL80211_BAND_5GHZ].rx; + cpu_to_le32(args->chains[NL80211_BAND_5GHZ].rx); + + ptr += WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams); + + if (is_ml_vdev) { + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + sizeof(*ml_params)); + ptr += TLV_HDR_SIZE; + ml_params = ptr; + + ml_params->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_VDEV_CREATE_PARAMS, + sizeof(*ml_params)); + ether_addr_copy(ml_params->mld_macaddr.addr, args->mld_addr); + } ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI vdev create: id %d type %d subtype %d macaddr %pM pdevid %d\n", @@ -974,14 +1045,32 @@ int ath12k_wmi_vdev_down(struct ath12k *ar, u8 vdev_id) static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan, struct wmi_vdev_start_req_arg *arg) { + u32 center_freq1 = arg->band_center_freq1; + memset(chan, 0, sizeof(*chan)); chan->mhz = cpu_to_le32(arg->freq); - chan->band_center_freq1 = cpu_to_le32(arg->band_center_freq1); - if (arg->mode == MODE_11AC_VHT80_80) + chan->band_center_freq1 = cpu_to_le32(center_freq1); + if (arg->mode == MODE_11BE_EHT320) { + if (arg->freq > center_freq1) + chan->band_center_freq1 = cpu_to_le32(center_freq1 + 80); + else + chan->band_center_freq1 = cpu_to_le32(center_freq1 - 80); + + chan->band_center_freq2 = cpu_to_le32(center_freq1); + + } else if (arg->mode == MODE_11BE_EHT160) { + if (arg->freq > center_freq1) + chan->band_center_freq1 = cpu_to_le32(center_freq1 + 40); + else + chan->band_center_freq1 = cpu_to_le32(center_freq1 - 40); + + chan->band_center_freq2 = cpu_to_le32(center_freq1); + } else if (arg->mode == MODE_11BE_EHT80_80) { chan->band_center_freq2 = cpu_to_le32(arg->band_center_freq2); - else + } else { chan->band_center_freq2 = 0; + } chan->info |= le32_encode_bits(arg->mode, WMI_CHAN_INFO_MODE); if (arg->passive) @@ -1014,19 +1103,27 @@ static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan, int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, bool restart) { + struct wmi_vdev_start_mlo_params *ml_params; + struct wmi_partner_link_info *partner_info; struct ath12k_wmi_pdev *wmi = ar->wmi; struct wmi_vdev_start_request_cmd *cmd; struct sk_buff *skb; struct ath12k_wmi_channel_params *chan; struct wmi_tlv *tlv; void *ptr; - int ret, len; + int ret, len, i, ml_arg_size = 0; if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) return -EINVAL; len = sizeof(*cmd) + sizeof(*chan) + TLV_HDR_SIZE; + if (!restart && arg->ml.enabled) { + ml_arg_size = TLV_HDR_SIZE + sizeof(*ml_params) + + TLV_HDR_SIZE + (arg->ml.num_partner_links * + sizeof(*partner_info)); + len += ml_arg_size; + } skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -1046,6 +1143,7 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, cmd->he_ops = cpu_to_le32(arg->he_ops); cmd->punct_bitmap = cpu_to_le32(arg->punct_bitmap); cmd->mbssid_flags = cpu_to_le32(arg->mbssid_flags); + cmd->mbssid_tx_vdev_id = cpu_to_le32(arg->mbssid_tx_vdev_id); if (!restart) { if (arg->ssid) { @@ -1078,6 +1176,61 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, ptr += sizeof(*tlv); + if (ml_arg_size) { + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + sizeof(*ml_params)); + ptr += TLV_HDR_SIZE; + + ml_params = ptr; + + ml_params->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_VDEV_START_PARAMS, + sizeof(*ml_params)); + + ml_params->flags = le32_encode_bits(arg->ml.enabled, + ATH12K_WMI_FLAG_MLO_ENABLED) | + le32_encode_bits(arg->ml.assoc_link, + ATH12K_WMI_FLAG_MLO_ASSOC_LINK) | + le32_encode_bits(arg->ml.mcast_link, + ATH12K_WMI_FLAG_MLO_MCAST_VDEV) | + le32_encode_bits(arg->ml.link_add, + ATH12K_WMI_FLAG_MLO_LINK_ADD); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "vdev %d start ml flags 0x%x\n", + arg->vdev_id, ml_params->flags); + + ptr += sizeof(*ml_params); + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + arg->ml.num_partner_links * + sizeof(*partner_info)); + ptr += TLV_HDR_SIZE; + + partner_info = ptr; + + for (i = 0; i < arg->ml.num_partner_links; i++) { + partner_info->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_PARTNER_LINK_PARAMS, + sizeof(*partner_info)); + partner_info->vdev_id = + cpu_to_le32(arg->ml.partner_info[i].vdev_id); + partner_info->hw_link_id = + cpu_to_le32(arg->ml.partner_info[i].hw_link_id); + ether_addr_copy(partner_info->vdev_addr.addr, + arg->ml.partner_info[i].addr); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "partner vdev %d hw_link_id %d macaddr%pM\n", + partner_info->vdev_id, partner_info->hw_link_id, + partner_info->vdev_addr.addr); + + partner_info++; + } + + ptr = partner_info; + } + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "vdev %s id 0x%x freq 0x%x mode 0x%x\n", restart ? "restart" : "start", arg->vdev_id, arg->freq, arg->mode); @@ -1097,7 +1250,7 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, return ret; } -int ath12k_wmi_vdev_up(struct ath12k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +int ath12k_wmi_vdev_up(struct ath12k *ar, struct ath12k_wmi_vdev_up_params *params) { struct ath12k_wmi_pdev *wmi = ar->wmi; struct wmi_vdev_up_cmd *cmd; @@ -1112,14 +1265,20 @@ int ath12k_wmi_vdev_up(struct ath12k *ar, u32 vdev_id, u32 aid, const u8 *bssid) cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_UP_CMD, sizeof(*cmd)); - cmd->vdev_id = cpu_to_le32(vdev_id); - cmd->vdev_assoc_id = cpu_to_le32(aid); + cmd->vdev_id = cpu_to_le32(params->vdev_id); + cmd->vdev_assoc_id = cpu_to_le32(params->aid); + + ether_addr_copy(cmd->vdev_bssid.addr, params->bssid); - ether_addr_copy(cmd->vdev_bssid.addr, bssid); + if (params->tx_bssid) { + ether_addr_copy(cmd->tx_vdev_bssid.addr, params->tx_bssid); + cmd->nontx_profile_idx = cpu_to_le32(params->nontx_profile_idx); + cmd->nontx_profile_cnt = cpu_to_le32(params->nontx_profile_cnt); + } ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI mgmt vdev up id 0x%x assoc id %d bssid %pM\n", - vdev_id, aid, bssid); + params->vdev_id, params->aid, params->bssid); ret = ath12k_wmi_cmd_send(wmi, skb, WMI_VDEV_UP_CMDID); if (ret) { @@ -1136,9 +1295,14 @@ int ath12k_wmi_send_peer_create_cmd(struct ath12k *ar, struct ath12k_wmi_pdev *wmi = ar->wmi; struct wmi_peer_create_cmd *cmd; struct sk_buff *skb; - int ret; + int ret, len; + struct wmi_peer_create_mlo_params *ml_param; + void *ptr; + struct wmi_tlv *tlv; - skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*ml_param); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -1150,9 +1314,23 @@ int ath12k_wmi_send_peer_create_cmd(struct ath12k *ar, cmd->peer_type = cpu_to_le32(arg->peer_type); cmd->vdev_id = cpu_to_le32(arg->vdev_id); + ptr = skb->data + sizeof(*cmd); + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + sizeof(*ml_param)); + ptr += TLV_HDR_SIZE; + ml_param = ptr; + ml_param->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_PEER_CREATE_PARAMS, + sizeof(*ml_param)); + if (arg->ml_enabled) + ml_param->flags = cpu_to_le32(ATH12K_WMI_FLAG_MLO_ENABLED); + + ptr += sizeof(*ml_param); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, - "WMI peer create vdev_id %d peer_addr %pM\n", - arg->vdev_id, arg->peer_addr); + "WMI peer create vdev_id %d peer_addr %pM ml_flags 0x%x\n", + arg->vdev_id, arg->peer_addr, ml_param->flags); ret = ath12k_wmi_cmd_send(wmi, skb, WMI_PEER_CREATE_CMDID); if (ret) { @@ -1525,6 +1703,7 @@ int ath12k_wmi_pdev_bss_chan_info_request(struct ath12k *ar, cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PDEV_BSS_CHAN_INFO_REQUEST, sizeof(*cmd)); cmd->req_type = cpu_to_le32(type); + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI bss chan info req type %d\n", type); @@ -1774,19 +1953,34 @@ int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id, return ret; } -int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, +int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, struct ieee80211_mutable_offsets *offs, - struct sk_buff *bcn) + struct sk_buff *bcn, + struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args) { + struct ath12k *ar = arvif->ar; struct ath12k_wmi_pdev *wmi = ar->wmi; + struct ath12k_base *ab = ar->ab; struct wmi_bcn_tmpl_cmd *cmd; struct ath12k_wmi_bcn_prb_info_params *bcn_prb_info; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *conf; + u32 vdev_id = arvif->vdev_id; struct wmi_tlv *tlv; struct sk_buff *skb; + u32 ema_params = 0; void *ptr; int ret, len; size_t aligned_len = roundup(bcn->len, 4); + conf = ath12k_mac_get_link_bss_conf(arvif); + if (!conf) { + ath12k_warn(ab, + "unable to access bss link conf in beacon template command for vif %pM link %u\n", + ahvif->vif->addr, arvif->link_id); + return -EINVAL; + } + len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); @@ -1798,9 +1992,27 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, sizeof(*cmd)); cmd->vdev_id = cpu_to_le32(vdev_id); cmd->tim_ie_offset = cpu_to_le32(offs->tim_offset); - cmd->csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[0]); - cmd->ext_csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[1]); + + if (conf->csa_active) { + cmd->csa_switch_count_offset = + cpu_to_le32(offs->cntdwn_counter_offs[0]); + cmd->ext_csa_switch_count_offset = + cpu_to_le32(offs->cntdwn_counter_offs[1]); + cmd->csa_event_bitmap = cpu_to_le32(0xFFFFFFFF); + arvif->current_cntdown_counter = bcn->data[offs->cntdwn_counter_offs[0]]; + } + cmd->buf_len = cpu_to_le32(bcn->len); + cmd->mbssid_ie_offset = cpu_to_le32(offs->mbssid_off); + if (ema_args) { + u32p_replace_bits(&ema_params, ema_args->bcn_cnt, WMI_EMA_BEACON_CNT); + u32p_replace_bits(&ema_params, ema_args->bcn_index, WMI_EMA_BEACON_IDX); + if (ema_args->bcn_index == 0) + u32p_replace_bits(&ema_params, 1, WMI_EMA_BEACON_FIRST); + if (ema_args->bcn_index + 1 == ema_args->bcn_cnt) + u32p_replace_bits(&ema_params, 1, WMI_EMA_BEACON_LAST); + cmd->ema_params = cpu_to_le32(ema_params); + } ptr = skb->data + sizeof(*cmd); @@ -1819,7 +2031,7 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, ret = ath12k_wmi_cmd_send(wmi, skb, WMI_BCN_TMPL_CMDID); if (ret) { - ath12k_warn(ar->ab, "failed to send WMI_BCN_TMPL_CMDID\n"); + ath12k_warn(ab, "failed to send WMI_BCN_TMPL_CMDID\n"); dev_kfree_skb(skb); } @@ -1900,7 +2112,7 @@ static void ath12k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd, if (arg->bw_160) cmd->peer_flags |= cpu_to_le32(WMI_PEER_160MHZ); if (arg->bw_320) - cmd->peer_flags |= cpu_to_le32(WMI_PEER_EXT_320MHZ); + cmd->peer_flags_ext |= cpu_to_le32(WMI_PEER_EXT_320MHZ); /* Typically if STBC is enabled for VHT it should be enabled * for HT as well @@ -1975,12 +2187,16 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, struct ath12k_wmi_vht_rate_set_params *mcs; struct ath12k_wmi_he_rate_set_params *he_mcs; struct ath12k_wmi_eht_rate_set_params *eht_mcs; + struct wmi_peer_assoc_mlo_params *ml_params; + struct wmi_peer_assoc_mlo_partner_info_params *partner_info; struct sk_buff *skb; struct wmi_tlv *tlv; void *ptr; - u32 peer_legacy_rates_align; - u32 peer_ht_rates_align; + u32 peer_legacy_rates_align, eml_pad_delay, eml_trans_delay; + u32 peer_ht_rates_align, eml_trans_timeout; int i, ret, len; + u16 eml_cap; + __le32 v; peer_legacy_rates_align = roundup(arg->peer_legacy_rates.num_rates, sizeof(u32)); @@ -1992,8 +2208,13 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, TLV_HDR_SIZE + (peer_ht_rates_align * sizeof(u8)) + sizeof(*mcs) + TLV_HDR_SIZE + (sizeof(*he_mcs) * arg->peer_he_mcs_count) + - TLV_HDR_SIZE + (sizeof(*eht_mcs) * arg->peer_eht_mcs_count) + - TLV_HDR_SIZE + TLV_HDR_SIZE; + TLV_HDR_SIZE + (sizeof(*eht_mcs) * arg->peer_eht_mcs_count); + + if (arg->ml.enabled) + len += TLV_HDR_SIZE + sizeof(*ml_params) + + TLV_HDR_SIZE + (arg->ml.num_partner_links * sizeof(*partner_info)); + else + len += (2 * TLV_HDR_SIZE); skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) @@ -2117,12 +2338,56 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, ptr += sizeof(*he_mcs); } - /* MLO header tag with 0 length */ - len = 0; tlv = ptr; + len = arg->ml.enabled ? sizeof(*ml_params) : 0; tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, len); ptr += TLV_HDR_SIZE; + if (!len) + goto skip_ml_params; + ml_params = ptr; + ml_params->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_PEER_ASSOC_PARAMS, + len); + ml_params->flags = cpu_to_le32(ATH12K_WMI_FLAG_MLO_ENABLED); + + if (arg->ml.assoc_link) + ml_params->flags |= cpu_to_le32(ATH12K_WMI_FLAG_MLO_ASSOC_LINK); + + if (arg->ml.primary_umac) + ml_params->flags |= cpu_to_le32(ATH12K_WMI_FLAG_MLO_PRIMARY_UMAC); + + if (arg->ml.logical_link_idx_valid) + ml_params->flags |= + cpu_to_le32(ATH12K_WMI_FLAG_MLO_LOGICAL_LINK_IDX_VALID); + + if (arg->ml.peer_id_valid) + ml_params->flags |= cpu_to_le32(ATH12K_WMI_FLAG_MLO_PEER_ID_VALID); + + ether_addr_copy(ml_params->mld_addr.addr, arg->ml.mld_addr); + ml_params->logical_link_idx = cpu_to_le32(arg->ml.logical_link_idx); + ml_params->ml_peer_id = cpu_to_le32(arg->ml.ml_peer_id); + ml_params->ieee_link_id = cpu_to_le32(arg->ml.ieee_link_id); + + eml_cap = arg->ml.eml_cap; + if (u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP)) { + /* Padding delay */ + eml_pad_delay = ieee80211_emlsr_pad_delay_in_us(eml_cap); + ml_params->emlsr_padding_delay_us = cpu_to_le32(eml_pad_delay); + /* Transition delay */ + eml_trans_delay = ieee80211_emlsr_trans_delay_in_us(eml_cap); + ml_params->emlsr_trans_delay_us = cpu_to_le32(eml_trans_delay); + /* Transition timeout */ + eml_trans_timeout = ieee80211_eml_trans_timeout_in_us(eml_cap); + ml_params->emlsr_trans_timeout_us = + cpu_to_le32(eml_trans_timeout); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi peer %pM emlsr padding delay %u, trans delay %u trans timeout %u", + arg->peer_mac, eml_pad_delay, eml_trans_delay, + eml_trans_timeout); + } + + ptr += sizeof(*ml_params); + +skip_ml_params: /* Loop through the EHT rate set */ len = arg->peer_eht_mcs_count * sizeof(*eht_mcs); tlv = ptr; @@ -2131,7 +2396,7 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, for (i = 0; i < arg->peer_eht_mcs_count; i++) { eht_mcs = ptr; - eht_mcs->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HE_RATE_SET, + eht_mcs->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_EHT_RATE_SET, sizeof(*eht_mcs)); eht_mcs->rx_mcs_set = cpu_to_le32(arg->peer_eht_rx_mcs_set[i]); @@ -2139,14 +2404,51 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, ptr += sizeof(*eht_mcs); } - /* ML partner links tag with 0 length */ - len = 0; + /* Update MCS15 capability */ + if (arg->eht_disable_mcs15) + cmd->peer_eht_ops = cpu_to_le32(IEEE80211_EHT_OPER_MCS15_DISABLE); + tlv = ptr; + len = arg->ml.enabled ? arg->ml.num_partner_links * sizeof(*partner_info) : 0; + /* fill ML Partner links */ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, len); ptr += TLV_HDR_SIZE; + if (len == 0) + goto send; + + for (i = 0; i < arg->ml.num_partner_links; i++) { + u32 cmd = WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC; + + partner_info = ptr; + partner_info->tlv_header = ath12k_wmi_tlv_cmd_hdr(cmd, + sizeof(*partner_info)); + partner_info->vdev_id = cpu_to_le32(arg->ml.partner_info[i].vdev_id); + partner_info->hw_link_id = + cpu_to_le32(arg->ml.partner_info[i].hw_link_id); + partner_info->flags = cpu_to_le32(ATH12K_WMI_FLAG_MLO_ENABLED); + + if (arg->ml.partner_info[i].assoc_link) + partner_info->flags |= + cpu_to_le32(ATH12K_WMI_FLAG_MLO_ASSOC_LINK); + + if (arg->ml.partner_info[i].primary_umac) + partner_info->flags |= + cpu_to_le32(ATH12K_WMI_FLAG_MLO_PRIMARY_UMAC); + + if (arg->ml.partner_info[i].logical_link_idx_valid) { + v = cpu_to_le32(ATH12K_WMI_FLAG_MLO_LINK_ID_VALID); + partner_info->flags |= v; + } + + partner_info->logical_link_idx = + cpu_to_le32(arg->ml.partner_info[i].logical_link_idx); + ptr += sizeof(*partner_info); + } + +send: ath12k_dbg(ar->ab, ATH12K_DBG_WMI, - "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x peer_flags_ext %x eht mac_cap %x %x eht phy_cap %x %x %x\n", + "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x peer_flags_ext %x eht mac_cap %x %x eht phy_cap %x %x %x peer_eht_ops %x\n", cmd->vdev_id, cmd->peer_associd, arg->peer_mac, cmd->peer_flags, cmd->peer_rate_caps, cmd->peer_caps, cmd->peer_listen_intval, cmd->peer_ht_caps, @@ -2159,7 +2461,7 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, cmd->peer_bw_rxnss_override, cmd->peer_flags_ext, cmd->peer_eht_cap_mac[0], cmd->peer_eht_cap_mac[1], cmd->peer_eht_cap_phy[0], cmd->peer_eht_cap_phy[1], - cmd->peer_eht_cap_phy[2]); + cmd->peer_eht_cap_phy[2], cmd->peer_eht_ops); ret = ath12k_wmi_cmd_send(wmi, skb, WMI_PEER_ASSOC_CMDID); if (ret) { @@ -2180,8 +2482,8 @@ void ath12k_wmi_start_scan_init(struct ath12k *ar, arg->dwell_time_active = 50; arg->dwell_time_active_2g = 0; arg->dwell_time_passive = 150; - arg->dwell_time_active_6g = 40; - arg->dwell_time_passive_6g = 30; + arg->dwell_time_active_6g = 70; + arg->dwell_time_passive_6g = 70; arg->min_rest_time = 50; arg->max_rest_time = 500; arg->repeat_probe_time = 0; @@ -2338,7 +2640,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, cmd->scan_id = cpu_to_le32(arg->scan_id); cmd->scan_req_id = cpu_to_le32(arg->scan_req_id); cmd->vdev_id = cpu_to_le32(arg->vdev_id); - cmd->scan_priority = cpu_to_le32(arg->scan_priority); + if (ar->state_11d == ATH12K_11D_PREPARING) + arg->scan_priority = WMI_SCAN_PRIORITY_MEDIUM; + else + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; cmd->notify_scan_events = cpu_to_le32(arg->notify_scan_events); ath12k_wmi_copy_scan_event_cntrl_flags(cmd, arg); @@ -2601,6 +2906,8 @@ int ath12k_wmi_send_scan_chan_list_cmd(struct ath12k *ar, WMI_CHAN_REG_INFO1_REG_CLS); *reg2 |= le32_encode_bits(channel_arg->antennamax, WMI_CHAN_REG_INFO2_ANT_MAX); + *reg2 |= le32_encode_bits(channel_arg->maxregpower, + WMI_CHAN_REG_INFO2_MAX_TX_PWR); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n", @@ -2723,6 +3030,149 @@ int ath12k_wmi_send_dfs_phyerr_offload_enable_cmd(struct ath12k *ar, return ret; } +int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, + const u8 *buf, size_t buf_len) +{ + struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab; + struct wmi_pdev_set_bios_interface_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + u8 *ptr; + u32 len, len_aligned; + int ret; + + len_aligned = roundup(buf_len, sizeof(u32)); + len = sizeof(*cmd) + TLV_HDR_SIZE + len_aligned; + + skb = ath12k_wmi_alloc_skb(wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_bios_interface_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PDEV_SET_BIOS_INTERFACE_CMD, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(WMI_PDEV_ID_SOC); + cmd->param_type_id = cpu_to_le32(param_id); + cmd->length = cpu_to_le32(buf_len); + + ptr = skb->data + sizeof(*cmd); + tlv = (struct wmi_tlv *)ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, len_aligned); + ptr += TLV_HDR_SIZE; + memcpy(ptr, buf, buf_len); + + ret = ath12k_wmi_cmd_send(&wmi_ab->wmi[0], + skb, + WMI_PDEV_SET_BIOS_INTERFACE_CMDID); + if (ret) { + ath12k_warn(ab, + "failed to send WMI_PDEV_SET_BIOS_INTERFACE_CMDID parameter id %d: %d\n", + param_id, ret); + dev_kfree_skb(skb); + } + + return 0; +} + +int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table) +{ + struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab; + struct wmi_pdev_set_bios_sar_table_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int ret; + u8 *buf_ptr; + u32 len, sar_table_len_aligned, sar_dbs_backoff_len_aligned; + const u8 *psar_value = psar_table + ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET; + const u8 *pdbs_value = psar_table + ATH12K_ACPI_DBS_BACKOFF_DATA_OFFSET; + + sar_table_len_aligned = roundup(ATH12K_ACPI_BIOS_SAR_TABLE_LEN, sizeof(u32)); + sar_dbs_backoff_len_aligned = roundup(ATH12K_ACPI_BIOS_SAR_DBS_BACKOFF_LEN, + sizeof(u32)); + len = sizeof(*cmd) + TLV_HDR_SIZE + sar_table_len_aligned + + TLV_HDR_SIZE + sar_dbs_backoff_len_aligned; + + skb = ath12k_wmi_alloc_skb(wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_bios_sar_table_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(WMI_PDEV_ID_SOC); + cmd->sar_len = cpu_to_le32(ATH12K_ACPI_BIOS_SAR_TABLE_LEN); + cmd->dbs_backoff_len = cpu_to_le32(ATH12K_ACPI_BIOS_SAR_DBS_BACKOFF_LEN); + + buf_ptr = skb->data + sizeof(*cmd); + tlv = (struct wmi_tlv *)buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, + sar_table_len_aligned); + buf_ptr += TLV_HDR_SIZE; + memcpy(buf_ptr, psar_value, ATH12K_ACPI_BIOS_SAR_TABLE_LEN); + + buf_ptr += sar_table_len_aligned; + tlv = (struct wmi_tlv *)buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, + sar_dbs_backoff_len_aligned); + buf_ptr += TLV_HDR_SIZE; + memcpy(buf_ptr, pdbs_value, ATH12K_ACPI_BIOS_SAR_DBS_BACKOFF_LEN); + + ret = ath12k_wmi_cmd_send(&wmi_ab->wmi[0], + skb, + WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID); + if (ret) { + ath12k_warn(ab, + "failed to send WMI_PDEV_SET_BIOS_INTERFACE_CMDID %d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table) +{ + struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab; + struct wmi_pdev_set_bios_geo_table_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int ret; + u8 *buf_ptr; + u32 len, sar_geo_len_aligned; + const u8 *pgeo_value = pgeo_table + ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET; + + sar_geo_len_aligned = roundup(ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN, sizeof(u32)); + len = sizeof(*cmd) + TLV_HDR_SIZE + sar_geo_len_aligned; + + skb = ath12k_wmi_alloc_skb(wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_bios_geo_table_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(WMI_PDEV_ID_SOC); + cmd->geo_len = cpu_to_le32(ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN); + + buf_ptr = skb->data + sizeof(*cmd); + tlv = (struct wmi_tlv *)buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, sar_geo_len_aligned); + buf_ptr += TLV_HDR_SIZE; + memcpy(buf_ptr, pgeo_value, ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN); + + ret = ath12k_wmi_cmd_send(&wmi_ab->wmi[0], + skb, + WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID); + if (ret) { + ath12k_warn(ab, + "failed to send WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID %d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + int ath12k_wmi_delba_send(struct ath12k *ar, u32 vdev_id, const u8 *mac, u32 tid, u32 initiator, u32 reason) { @@ -2915,6 +3365,110 @@ out: return ret; } +int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, + struct wmi_set_current_country_arg *arg) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_set_current_country_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_current_country_cmd *)skb->data; + cmd->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_SET_CURRENT_COUNTRY_CMD, + sizeof(*cmd)); + + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + memcpy(&cmd->new_alpha2, &arg->alpha2, sizeof(arg->alpha2)); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_SET_CURRENT_COUNTRY_CMDID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "set current country pdev id %d alpha2 %c%c\n", + ar->pdev->pdev_id, + arg->alpha2[0], + arg->alpha2[1]); + + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_SET_CURRENT_COUNTRY_CMDID: %d\n", ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, + struct wmi_11d_scan_start_arg *arg) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_11d_scan_start_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_11d_scan_start_cmd *)skb->data; + cmd->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_START_CMD, + sizeof(*cmd)); + + cmd->vdev_id = cpu_to_le32(arg->vdev_id); + cmd->scan_period_msec = cpu_to_le32(arg->scan_period_msec); + cmd->start_interval_msec = cpu_to_le32(arg->start_interval_msec); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "send 11d scan start vdev id %d period %d ms internal %d ms\n", + arg->vdev_id, arg->scan_period_msec, + arg->start_interval_msec); + + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_11d_scan_stop_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_11d_scan_stop_cmd *)skb->data; + cmd->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_STOP_CMD, + sizeof(*cmd)); + + cmd->vdev_id = cpu_to_le32(vdev_id); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "send 11d scan stop vdev id %d\n", + cmd->vdev_id); + + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret); + dev_kfree_skb(skb); + } + + return ret; +} + int ath12k_wmi_send_twt_enable_cmd(struct ath12k *ar, u32 pdev_id) { @@ -3248,15 +3802,15 @@ ath12k_fill_band_to_mac_param(struct ath12k_base *soc, arg[i].pdev_id = pdev->pdev_id; switch (pdev->cap.supported_bands) { - case WMI_HOST_WLAN_2G_5G_CAP: + case WMI_HOST_WLAN_2GHZ_5GHZ_CAP: arg[i].start_freq = hal_reg_cap->low_2ghz_chan; arg[i].end_freq = hal_reg_cap->high_5ghz_chan; break; - case WMI_HOST_WLAN_2G_CAP: + case WMI_HOST_WLAN_2GHZ_CAP: arg[i].start_freq = hal_reg_cap->low_2ghz_chan; arg[i].end_freq = hal_reg_cap->high_2ghz_chan; break; - case WMI_HOST_WLAN_5G_CAP: + case WMI_HOST_WLAN_5GHZ_CAP: arg[i].start_freq = hal_reg_cap->low_5ghz_chan; arg[i].end_freq = hal_reg_cap->high_5ghz_chan; break; @@ -3267,7 +3821,8 @@ ath12k_fill_band_to_mac_param(struct ath12k_base *soc, } static void -ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cfg, +ath12k_wmi_copy_resource_config(struct ath12k_base *ab, + struct ath12k_wmi_resource_config_params *wmi_cfg, struct ath12k_wmi_resource_config_arg *tg_cfg) { wmi_cfg->num_vdevs = cpu_to_le32(tg_cfg->num_vdevs); @@ -3324,16 +3879,22 @@ ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cf wmi_cfg->bpf_instruction_size = cpu_to_le32(tg_cfg->bpf_instruction_size); wmi_cfg->max_bssid_rx_filters = cpu_to_le32(tg_cfg->max_bssid_rx_filters); wmi_cfg->use_pdev_id = cpu_to_le32(tg_cfg->use_pdev_id); - wmi_cfg->flag1 = cpu_to_le32(tg_cfg->atf_config); + wmi_cfg->flag1 = cpu_to_le32(tg_cfg->atf_config | + WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64); wmi_cfg->peer_map_unmap_version = cpu_to_le32(tg_cfg->peer_map_unmap_version); wmi_cfg->sched_params = cpu_to_le32(tg_cfg->sched_params); wmi_cfg->twt_ap_pdev_count = cpu_to_le32(tg_cfg->twt_ap_pdev_count); wmi_cfg->twt_ap_sta_count = cpu_to_le32(tg_cfg->twt_ap_sta_count); - wmi_cfg->flags2 = le32_encode_bits(tg_cfg->dp_peer_meta_data_ver, + wmi_cfg->flags2 = le32_encode_bits(tg_cfg->peer_metadata_ver, WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION); - wmi_cfg->host_service_flags = cpu_to_le32(tg_cfg->is_reg_cc_ext_event_supported << WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT); + if (ab->hw_params->reoq_lut_support) + wmi_cfg->host_service_flags |= + cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT); + wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt); + wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); + wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, @@ -3371,7 +3932,7 @@ static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, ptr = skb->data + sizeof(*cmd); cfg = ptr; - ath12k_wmi_copy_resource_config(cfg, &arg->res_cfg); + ath12k_wmi_copy_resource_config(ab, cfg, &arg->res_cfg); cfg->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_RESOURCE_CONFIG, sizeof(*cfg)); @@ -3546,6 +4107,7 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) arg.res_cfg.is_reg_cc_ext_event_supported = true; ab->hw_params->wmi_init(ab, &arg.res_cfg); + ab->wow.wmi_conf_rx_decap_mode = arg.res_cfg.rx_decap_mode; arg.num_mem_chunks = wmi_ab->num_mem_chunks; arg.hw_mode_id = wmi_ab->preferred_hw_mode; @@ -3557,6 +4119,8 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) arg.num_band_to_mac = ab->num_radios; ath12k_fill_band_to_mac_param(ab, arg.band_to_mac); + ab->dp.peer_metadata_ver = arg.res_cfg.peer_metadata_ver; + return ath12k_init_cmd_send(&wmi_ab->wmi[0], &arg); } @@ -3664,7 +4228,7 @@ int ath12k_wmi_pdev_dma_ring_cfg(struct ath12k *ar, cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_DMA_RING_CFG_REQ, sizeof(*cmd)); - cmd->pdev_id = cpu_to_le32(DP_SW2HW_MACID(arg->pdev_id)); + cmd->pdev_id = cpu_to_le32(arg->pdev_id); cmd->module_id = cpu_to_le32(arg->module_id); cmd->base_paddr_lo = cpu_to_le32(arg->base_paddr_lo); cmd->base_paddr_hi = cpu_to_le32(arg->base_paddr_hi); @@ -3836,6 +4400,7 @@ static int ath12k_wmi_hw_mode_caps_parse(struct ath12k_base *soc, static int ath12k_wmi_hw_mode_caps(struct ath12k_base *soc, u16 len, const void *ptr, void *data) { + struct ath12k_svc_ext_info *svc_ext_info = &soc->wmi_ab.svc_ext_info; struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext = data; const struct ath12k_wmi_hw_mode_cap_params *hw_mode_caps; enum wmi_host_hw_mode_config_type mode, pref; @@ -3868,8 +4433,11 @@ static int ath12k_wmi_hw_mode_caps(struct ath12k_base *soc, } } - ath12k_dbg(soc, ATH12K_DBG_WMI, "preferred_hw_mode:%d\n", - soc->wmi_ab.preferred_hw_mode); + svc_ext_info->num_hw_modes = svc_rdy_ext->n_hw_mode_caps; + + ath12k_dbg(soc, ATH12K_DBG_WMI, "num hw modes %u preferred_hw_mode %d\n", + svc_ext_info->num_hw_modes, soc->wmi_ab.preferred_hw_mode); + if (soc->wmi_ab.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) return -EINVAL; @@ -4041,6 +4609,7 @@ static void ath12k_wmi_free_dbring_caps(struct ath12k_base *ab) { kfree(ab->db_caps); ab->db_caps = NULL; + ab->num_db_cap = 0; } static int ath12k_wmi_dma_ring_caps(struct ath12k_base *ab, @@ -4098,6 +4667,65 @@ free_dir_buff: return ret; } +static void +ath12k_wmi_save_mac_phy_info(struct ath12k_base *ab, + const struct ath12k_wmi_mac_phy_caps_params *mac_phy_cap, + struct ath12k_svc_ext_mac_phy_info *mac_phy_info) +{ + mac_phy_info->phy_id = __le32_to_cpu(mac_phy_cap->phy_id); + mac_phy_info->supported_bands = __le32_to_cpu(mac_phy_cap->supported_bands); + mac_phy_info->hw_freq_range.low_2ghz_freq = + __le32_to_cpu(mac_phy_cap->low_2ghz_chan_freq); + mac_phy_info->hw_freq_range.high_2ghz_freq = + __le32_to_cpu(mac_phy_cap->high_2ghz_chan_freq); + mac_phy_info->hw_freq_range.low_5ghz_freq = + __le32_to_cpu(mac_phy_cap->low_5ghz_chan_freq); + mac_phy_info->hw_freq_range.high_5ghz_freq = + __le32_to_cpu(mac_phy_cap->high_5ghz_chan_freq); +} + +static void +ath12k_wmi_save_all_mac_phy_info(struct ath12k_base *ab, + struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext) +{ + struct ath12k_svc_ext_info *svc_ext_info = &ab->wmi_ab.svc_ext_info; + const struct ath12k_wmi_mac_phy_caps_params *mac_phy_cap; + const struct ath12k_wmi_hw_mode_cap_params *hw_mode_cap; + struct ath12k_svc_ext_mac_phy_info *mac_phy_info; + u32 hw_mode_id, phy_bit_map; + u8 hw_idx; + + mac_phy_info = &svc_ext_info->mac_phy_info[0]; + mac_phy_cap = svc_rdy_ext->mac_phy_caps; + + for (hw_idx = 0; hw_idx < svc_ext_info->num_hw_modes; hw_idx++) { + hw_mode_cap = &svc_rdy_ext->hw_mode_caps[hw_idx]; + hw_mode_id = __le32_to_cpu(hw_mode_cap->hw_mode_id); + phy_bit_map = __le32_to_cpu(hw_mode_cap->phy_id_map); + + while (phy_bit_map) { + ath12k_wmi_save_mac_phy_info(ab, mac_phy_cap, mac_phy_info); + mac_phy_info->hw_mode_config_type = + le32_get_bits(hw_mode_cap->hw_mode_config_type, + WMI_HW_MODE_CAP_CFG_TYPE); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "hw_idx %u hw_mode_id %u hw_mode_config_type %u supported_bands %u phy_id %u 2 GHz [%u - %u] 5 GHz [%u - %u]\n", + hw_idx, hw_mode_id, + mac_phy_info->hw_mode_config_type, + mac_phy_info->supported_bands, mac_phy_info->phy_id, + mac_phy_info->hw_freq_range.low_2ghz_freq, + mac_phy_info->hw_freq_range.high_2ghz_freq, + mac_phy_info->hw_freq_range.low_5ghz_freq, + mac_phy_info->hw_freq_range.high_5ghz_freq); + + mac_phy_cap++; + mac_phy_info++; + + phy_bit_map >>= 1; + } + } +} + static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data) @@ -4146,6 +4774,8 @@ static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab, return ret; } + ath12k_wmi_save_all_mac_phy_info(ab, svc_rdy_ext); + svc_rdy_ext->mac_phy_done = true; } else if (!svc_rdy_ext->ext_hal_reg_done) { ret = ath12k_wmi_ext_hal_reg_caps(ab, len, ptr, svc_rdy_ext); @@ -4196,6 +4826,7 @@ static int ath12k_service_ready_ext_event(struct ath12k_base *ab, return 0; err: + kfree(svc_rdy_ext.mac_phy_caps); ath12k_wmi_free_dbring_caps(ab); return ret; } @@ -4294,7 +4925,7 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab, bands = pdev->cap.supported_bands; } - if (bands & WMI_HOST_WLAN_2G_CAP) { + if (bands & WMI_HOST_WLAN_2GHZ_CAP) { ath12k_wmi_eht_caps_parse(pdev, NL80211_BAND_2GHZ, caps->eht_cap_mac_info_2ghz, caps->eht_cap_phy_info_2ghz, @@ -4303,7 +4934,7 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab, caps->eht_cap_info_internal); } - if (bands & WMI_HOST_WLAN_5G_CAP) { + if (bands & WMI_HOST_WLAN_5GHZ_CAP) { ath12k_wmi_eht_caps_parse(pdev, NL80211_BAND_5GHZ, caps->eht_cap_mac_info_5ghz, caps->eht_cap_phy_info_5ghz, @@ -4319,6 +4950,9 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab, caps->eht_cap_info_internal); } + pdev->cap.eml_cap = le32_to_cpu(caps->eml_capability); + pdev->cap.mld_cap = le32_to_cpu(caps->mld_capability); + return 0; } @@ -4358,10 +4992,449 @@ static int ath12k_wmi_tlv_mac_phy_caps_ext(struct ath12k_base *ab, u16 tag, return 0; } +static void +ath12k_wmi_update_freq_info(struct ath12k_base *ab, + struct ath12k_svc_ext_mac_phy_info *mac_cap, + enum ath12k_hw_mode mode, + u32 phy_id) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *mac_range; + + mac_range = &hw_mode_info->freq_range_caps[mode][phy_id]; + + if (mac_cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { + mac_range->low_2ghz_freq = max_t(u32, + mac_cap->hw_freq_range.low_2ghz_freq, + ATH12K_MIN_2GHZ_FREQ); + mac_range->high_2ghz_freq = mac_cap->hw_freq_range.high_2ghz_freq ? + min_t(u32, + mac_cap->hw_freq_range.high_2ghz_freq, + ATH12K_MAX_2GHZ_FREQ) : + ATH12K_MAX_2GHZ_FREQ; + } + + if (mac_cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { + mac_range->low_5ghz_freq = max_t(u32, + mac_cap->hw_freq_range.low_5ghz_freq, + ATH12K_MIN_5GHZ_FREQ); + mac_range->high_5ghz_freq = mac_cap->hw_freq_range.high_5ghz_freq ? + min_t(u32, + mac_cap->hw_freq_range.high_5ghz_freq, + ATH12K_MAX_6GHZ_FREQ) : + ATH12K_MAX_6GHZ_FREQ; + } +} + +static bool +ath12k_wmi_all_phy_range_updated(struct ath12k_base *ab, + enum ath12k_hw_mode hwmode) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *mac_range; + u8 phy_id; + + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + mac_range = &hw_mode_info->freq_range_caps[hwmode][phy_id]; + /* modify SBS/DBS range only when both phy for DBS are filled */ + if (!mac_range->low_2ghz_freq && !mac_range->low_5ghz_freq) + return false; + } + + return true; +} + +static void ath12k_wmi_update_dbs_freq_info(struct ath12k_base *ab) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *mac_range; + u8 phy_id; + + mac_range = hw_mode_info->freq_range_caps[ATH12K_HW_MODE_DBS]; + /* Reset 5 GHz range for shared mac for DBS */ + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + if (mac_range[phy_id].low_2ghz_freq && + mac_range[phy_id].low_5ghz_freq) { + mac_range[phy_id].low_5ghz_freq = 0; + mac_range[phy_id].high_5ghz_freq = 0; + } + } +} + +static u32 +ath12k_wmi_get_highest_5ghz_freq_from_range(struct ath12k_hw_mode_freq_range_arg *range) +{ + u32 highest_freq = 0; + u8 phy_id; + + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + if (range[phy_id].high_5ghz_freq > highest_freq) + highest_freq = range[phy_id].high_5ghz_freq; + } + + return highest_freq ? highest_freq : ATH12K_MAX_6GHZ_FREQ; +} + +static u32 +ath12k_wmi_get_lowest_5ghz_freq_from_range(struct ath12k_hw_mode_freq_range_arg *range) +{ + u32 lowest_freq = 0; + u8 phy_id; + + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + if ((!lowest_freq && range[phy_id].low_5ghz_freq) || + range[phy_id].low_5ghz_freq < lowest_freq) + lowest_freq = range[phy_id].low_5ghz_freq; + } + + return lowest_freq ? lowest_freq : ATH12K_MIN_5GHZ_FREQ; +} + +static void +ath12k_wmi_fill_upper_share_sbs_freq(struct ath12k_base *ab, + u16 sbs_range_sep, + struct ath12k_hw_mode_freq_range_arg *ref_freq) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *upper_sbs_freq_range; + u8 phy_id; + + upper_sbs_freq_range = + hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS_UPPER_SHARE]; + + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + upper_sbs_freq_range[phy_id].low_2ghz_freq = + ref_freq[phy_id].low_2ghz_freq; + upper_sbs_freq_range[phy_id].high_2ghz_freq = + ref_freq[phy_id].high_2ghz_freq; + + /* update for shared mac */ + if (upper_sbs_freq_range[phy_id].low_2ghz_freq) { + upper_sbs_freq_range[phy_id].low_5ghz_freq = sbs_range_sep + 10; + upper_sbs_freq_range[phy_id].high_5ghz_freq = + ath12k_wmi_get_highest_5ghz_freq_from_range(ref_freq); + } else { + upper_sbs_freq_range[phy_id].low_5ghz_freq = + ath12k_wmi_get_lowest_5ghz_freq_from_range(ref_freq); + upper_sbs_freq_range[phy_id].high_5ghz_freq = sbs_range_sep; + } + } +} + +static void +ath12k_wmi_fill_lower_share_sbs_freq(struct ath12k_base *ab, + u16 sbs_range_sep, + struct ath12k_hw_mode_freq_range_arg *ref_freq) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *lower_sbs_freq_range; + u8 phy_id; + + lower_sbs_freq_range = + hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS_LOWER_SHARE]; + + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + lower_sbs_freq_range[phy_id].low_2ghz_freq = + ref_freq[phy_id].low_2ghz_freq; + lower_sbs_freq_range[phy_id].high_2ghz_freq = + ref_freq[phy_id].high_2ghz_freq; + + /* update for shared mac */ + if (lower_sbs_freq_range[phy_id].low_2ghz_freq) { + lower_sbs_freq_range[phy_id].low_5ghz_freq = + ath12k_wmi_get_lowest_5ghz_freq_from_range(ref_freq); + lower_sbs_freq_range[phy_id].high_5ghz_freq = sbs_range_sep; + } else { + lower_sbs_freq_range[phy_id].low_5ghz_freq = sbs_range_sep + 10; + lower_sbs_freq_range[phy_id].high_5ghz_freq = + ath12k_wmi_get_highest_5ghz_freq_from_range(ref_freq); + } + } +} + +static const char *ath12k_wmi_hw_mode_to_str(enum ath12k_hw_mode hw_mode) +{ + static const char * const mode_str[] = { + [ATH12K_HW_MODE_SMM] = "SMM", + [ATH12K_HW_MODE_DBS] = "DBS", + [ATH12K_HW_MODE_SBS] = "SBS", + [ATH12K_HW_MODE_SBS_UPPER_SHARE] = "SBS_UPPER_SHARE", + [ATH12K_HW_MODE_SBS_LOWER_SHARE] = "SBS_LOWER_SHARE", + }; + + if (hw_mode >= ARRAY_SIZE(mode_str)) + return "Unknown"; + + return mode_str[hw_mode]; +} + +static void +ath12k_wmi_dump_freq_range_per_mac(struct ath12k_base *ab, + struct ath12k_hw_mode_freq_range_arg *freq_range, + enum ath12k_hw_mode hw_mode) +{ + u8 i; + + for (i = 0; i < MAX_RADIOS; i++) + if (freq_range[i].low_2ghz_freq || freq_range[i].low_5ghz_freq) + ath12k_dbg(ab, ATH12K_DBG_WMI, + "frequency range: %s(%d) mac %d 2 GHz [%d - %d] 5 GHz [%d - %d]", + ath12k_wmi_hw_mode_to_str(hw_mode), + hw_mode, i, + freq_range[i].low_2ghz_freq, + freq_range[i].high_2ghz_freq, + freq_range[i].low_5ghz_freq, + freq_range[i].high_5ghz_freq); +} + +static void ath12k_wmi_dump_freq_range(struct ath12k_base *ab) +{ + struct ath12k_hw_mode_freq_range_arg *freq_range; + u8 i; + + for (i = ATH12K_HW_MODE_SMM; i < ATH12K_HW_MODE_MAX; i++) { + freq_range = ab->wmi_ab.hw_mode_info.freq_range_caps[i]; + ath12k_wmi_dump_freq_range_per_mac(ab, freq_range, i); + } +} + +static int ath12k_wmi_modify_sbs_freq(struct ath12k_base *ab, u8 phy_id) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *sbs_mac_range, *shared_mac_range; + struct ath12k_hw_mode_freq_range_arg *non_shared_range; + u8 shared_phy_id; + + sbs_mac_range = &hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS][phy_id]; + + /* if SBS mac range has both 2.4 and 5 GHz ranges, i.e. shared phy_id + * keep the range as it is in SBS + */ + if (sbs_mac_range->low_2ghz_freq && sbs_mac_range->low_5ghz_freq) + return 0; + + if (sbs_mac_range->low_2ghz_freq && !sbs_mac_range->low_5ghz_freq) { + ath12k_err(ab, "Invalid DBS/SBS mode with only 2.4Ghz"); + ath12k_wmi_dump_freq_range_per_mac(ab, sbs_mac_range, ATH12K_HW_MODE_SBS); + return -EINVAL; + } + + non_shared_range = sbs_mac_range; + /* if SBS mac range has only 5 GHz then it's the non-shared phy, so + * modify the range as per the shared mac. + */ + shared_phy_id = phy_id ? 0 : 1; + shared_mac_range = + &hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS][shared_phy_id]; + + if (shared_mac_range->low_5ghz_freq > non_shared_range->low_5ghz_freq) { + ath12k_dbg(ab, ATH12K_DBG_WMI, "high 5 GHz shared"); + /* If the shared mac lower 5 GHz frequency is greater than + * non-shared mac lower 5 GHz frequency then the shared mac has + * high 5 GHz shared with 2.4 GHz. So non-shared mac's 5 GHz high + * freq should be less than the shared mac's low 5 GHz freq. + */ + if (non_shared_range->high_5ghz_freq >= + shared_mac_range->low_5ghz_freq) + non_shared_range->high_5ghz_freq = + max_t(u32, shared_mac_range->low_5ghz_freq - 10, + non_shared_range->low_5ghz_freq); + } else if (shared_mac_range->high_5ghz_freq < + non_shared_range->high_5ghz_freq) { + ath12k_dbg(ab, ATH12K_DBG_WMI, "low 5 GHz shared"); + /* If the shared mac high 5 GHz frequency is less than + * non-shared mac high 5 GHz frequency then the shared mac has + * low 5 GHz shared with 2.4 GHz. So non-shared mac's 5 GHz low + * freq should be greater than the shared mac's high 5 GHz freq. + */ + if (shared_mac_range->high_5ghz_freq >= + non_shared_range->low_5ghz_freq) + non_shared_range->low_5ghz_freq = + min_t(u32, shared_mac_range->high_5ghz_freq + 10, + non_shared_range->high_5ghz_freq); + } else { + ath12k_warn(ab, "invalid SBS range with all 5 GHz shared"); + return -EINVAL; + } + + return 0; +} + +static void ath12k_wmi_update_sbs_freq_info(struct ath12k_base *ab) +{ + struct ath12k_hw_mode_info *hw_mode_info = &ab->wmi_ab.hw_mode_info; + struct ath12k_hw_mode_freq_range_arg *mac_range; + u16 sbs_range_sep; + u8 phy_id; + int ret; + + mac_range = hw_mode_info->freq_range_caps[ATH12K_HW_MODE_SBS]; + + /* If sbs_lower_band_end_freq has a value, then the frequency range + * will be split using that value. + */ + sbs_range_sep = ab->wmi_ab.sbs_lower_band_end_freq; + if (sbs_range_sep) { + ath12k_wmi_fill_upper_share_sbs_freq(ab, sbs_range_sep, + mac_range); + ath12k_wmi_fill_lower_share_sbs_freq(ab, sbs_range_sep, + mac_range); + /* Hardware specifies the range boundary with sbs_range_sep, + * (i.e. the boundary between 5 GHz high and 5 GHz low), + * reset the original one to make sure it will not get used. + */ + memset(mac_range, 0, sizeof(*mac_range) * MAX_RADIOS); + return; + } + + /* If sbs_lower_band_end_freq is not set that means firmware will send one + * shared mac range and one non-shared mac range. so update that freq. + */ + for (phy_id = 0; phy_id < MAX_RADIOS; phy_id++) { + ret = ath12k_wmi_modify_sbs_freq(ab, phy_id); + if (ret) { + memset(mac_range, 0, sizeof(*mac_range) * MAX_RADIOS); + break; + } + } +} + +static void +ath12k_wmi_update_mac_freq_info(struct ath12k_base *ab, + enum wmi_host_hw_mode_config_type hw_config_type, + u32 phy_id, + struct ath12k_svc_ext_mac_phy_info *mac_cap) +{ + if (phy_id >= MAX_RADIOS) { + ath12k_err(ab, "mac more than two not supported: %d", phy_id); + return; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "hw_mode_cfg %d mac %d band 0x%x SBS cutoff freq %d 2 GHz [%d - %d] 5 GHz [%d - %d]", + hw_config_type, phy_id, mac_cap->supported_bands, + ab->wmi_ab.sbs_lower_band_end_freq, + mac_cap->hw_freq_range.low_2ghz_freq, + mac_cap->hw_freq_range.high_2ghz_freq, + mac_cap->hw_freq_range.low_5ghz_freq, + mac_cap->hw_freq_range.high_5ghz_freq); + + switch (hw_config_type) { + case WMI_HOST_HW_MODE_SINGLE: + if (phy_id) { + ath12k_dbg(ab, ATH12K_DBG_WMI, "mac phy 1 is not supported"); + break; + } + ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_SMM, phy_id); + break; + + case WMI_HOST_HW_MODE_DBS: + if (!ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_DBS)) + ath12k_wmi_update_freq_info(ab, mac_cap, + ATH12K_HW_MODE_DBS, phy_id); + break; + case WMI_HOST_HW_MODE_DBS_SBS: + case WMI_HOST_HW_MODE_DBS_OR_SBS: + ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_DBS, phy_id); + if (ab->wmi_ab.sbs_lower_band_end_freq || + mac_cap->hw_freq_range.low_5ghz_freq || + mac_cap->hw_freq_range.low_2ghz_freq) + ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_SBS, + phy_id); + + if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_DBS)) + ath12k_wmi_update_dbs_freq_info(ab); + if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS)) + ath12k_wmi_update_sbs_freq_info(ab); + break; + case WMI_HOST_HW_MODE_SBS: + case WMI_HOST_HW_MODE_SBS_PASSIVE: + ath12k_wmi_update_freq_info(ab, mac_cap, ATH12K_HW_MODE_SBS, phy_id); + if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS)) + ath12k_wmi_update_sbs_freq_info(ab); + + break; + default: + break; + } +} + +static bool ath12k_wmi_sbs_range_present(struct ath12k_base *ab) +{ + if (ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS) || + (ab->wmi_ab.sbs_lower_band_end_freq && + ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS_LOWER_SHARE) && + ath12k_wmi_all_phy_range_updated(ab, ATH12K_HW_MODE_SBS_UPPER_SHARE))) + return true; + + return false; +} + +static int ath12k_wmi_update_hw_mode_list(struct ath12k_base *ab) +{ + struct ath12k_svc_ext_info *svc_ext_info = &ab->wmi_ab.svc_ext_info; + struct ath12k_hw_mode_info *info = &ab->wmi_ab.hw_mode_info; + enum wmi_host_hw_mode_config_type hw_config_type; + struct ath12k_svc_ext_mac_phy_info *tmp; + bool dbs_mode = false, sbs_mode = false; + u32 i, j = 0; + + if (!svc_ext_info->num_hw_modes) { + ath12k_err(ab, "invalid number of hw modes"); + return -EINVAL; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, "updated HW mode list: num modes %d", + svc_ext_info->num_hw_modes); + + memset(info->freq_range_caps, 0, sizeof(info->freq_range_caps)); + + for (i = 0; i < svc_ext_info->num_hw_modes; i++) { + if (j >= ATH12K_MAX_MAC_PHY_CAP) + return -EINVAL; + + /* Update for MAC0 */ + tmp = &svc_ext_info->mac_phy_info[j++]; + hw_config_type = tmp->hw_mode_config_type; + ath12k_wmi_update_mac_freq_info(ab, hw_config_type, tmp->phy_id, tmp); + + /* SBS and DBS have dual MAC. Up to 2 MACs are considered. */ + if (hw_config_type == WMI_HOST_HW_MODE_DBS || + hw_config_type == WMI_HOST_HW_MODE_SBS_PASSIVE || + hw_config_type == WMI_HOST_HW_MODE_SBS || + hw_config_type == WMI_HOST_HW_MODE_DBS_OR_SBS) { + if (j >= ATH12K_MAX_MAC_PHY_CAP) + return -EINVAL; + /* Update for MAC1 */ + tmp = &svc_ext_info->mac_phy_info[j++]; + ath12k_wmi_update_mac_freq_info(ab, hw_config_type, + tmp->phy_id, tmp); + + if (hw_config_type == WMI_HOST_HW_MODE_DBS || + hw_config_type == WMI_HOST_HW_MODE_DBS_OR_SBS) + dbs_mode = true; + + if (ath12k_wmi_sbs_range_present(ab) && + (hw_config_type == WMI_HOST_HW_MODE_SBS_PASSIVE || + hw_config_type == WMI_HOST_HW_MODE_SBS || + hw_config_type == WMI_HOST_HW_MODE_DBS_OR_SBS)) + sbs_mode = true; + } + } + + info->support_dbs = dbs_mode; + info->support_sbs = sbs_mode; + + ath12k_wmi_dump_freq_range(ab); + + return 0; +} + static int ath12k_wmi_svc_rdy_ext2_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data) { + const struct ath12k_wmi_dbs_or_sbs_cap_params *dbs_or_sbs_caps; struct ath12k_wmi_pdev *wmi_handle = &ab->wmi_ab.wmi[0]; struct ath12k_wmi_svc_rdy_ext2_parse *parse = data; int ret; @@ -4403,7 +5476,32 @@ static int ath12k_wmi_svc_rdy_ext2_parse(struct ath12k_base *ab, } parse->mac_phy_caps_ext_done = true; + } else if (!parse->hal_reg_caps_ext2_done) { + parse->hal_reg_caps_ext2_done = true; + } else if (!parse->scan_radio_caps_ext2_done) { + parse->scan_radio_caps_ext2_done = true; + } else if (!parse->twt_caps_done) { + parse->twt_caps_done = true; + } else if (!parse->htt_msdu_idx_to_qtype_map_done) { + parse->htt_msdu_idx_to_qtype_map_done = true; + } else if (!parse->dbs_or_sbs_cap_ext_done) { + dbs_or_sbs_caps = ptr; + ab->wmi_ab.sbs_lower_band_end_freq = + __le32_to_cpu(dbs_or_sbs_caps->sbs_lower_band_end_freq); + + ath12k_dbg(ab, ATH12K_DBG_WMI, "sbs_lower_band_end_freq %u\n", + ab->wmi_ab.sbs_lower_band_end_freq); + + ret = ath12k_wmi_update_hw_mode_list(ab); + if (ret) { + ath12k_warn(ab, "failed to update hw mode list: %d\n", + ret); + return ret; + } + + parse->dbs_or_sbs_cap_ext_done = true; } + break; default: break; @@ -4505,6 +5603,22 @@ static struct ath12k_reg_rule return reg_rule_ptr; } +static u8 ath12k_wmi_ignore_num_extra_rules(struct ath12k_wmi_reg_rule_ext_params *rule, + u32 num_reg_rules) +{ + u8 num_invalid_5ghz_rules = 0; + u32 count, start_freq; + + for (count = 0; count < num_reg_rules; count++) { + start_freq = le32_get_bits(rule[count].freq_info, REG_RULE_START_FREQ); + + if (start_freq >= ATH12K_MIN_6GHZ_FREQ) + num_invalid_5ghz_rules++; + } + + return num_invalid_5ghz_rules; +} + static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, struct sk_buff *skb, struct ath12k_reg_info *reg_info) @@ -4515,6 +5629,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, u32 num_2g_reg_rules, num_5g_reg_rules; u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; u32 num_6g_reg_rules_cl[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u8 num_invalid_5ghz_ext_rules; u32 total_reg_rules = 0; int ret, i, j; @@ -4567,9 +5682,9 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { num_6g_reg_rules_ap[i] = reg_info->num_6g_reg_rules_ap[i]; - if (num_6g_reg_rules_ap[i] > MAX_6G_REG_RULES) { + if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n", - i, num_6g_reg_rules_ap[i], MAX_6G_REG_RULES); + i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES); kfree(tb); return -EINVAL; } @@ -4590,9 +5705,9 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, reg_info->num_6g_reg_rules_cl[WMI_REG_VLP_AP][i]; total_reg_rules += num_6g_reg_rules_cl[WMI_REG_VLP_AP][i]; - if (num_6g_reg_rules_cl[WMI_REG_INDOOR_AP][i] > MAX_6G_REG_RULES || - num_6g_reg_rules_cl[WMI_REG_STD_POWER_AP][i] > MAX_6G_REG_RULES || - num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6G_REG_RULES) { + if (num_6g_reg_rules_cl[WMI_REG_INDOOR_AP][i] > MAX_6GHZ_REG_RULES || + num_6g_reg_rules_cl[WMI_REG_STD_POWER_AP][i] > MAX_6GHZ_REG_RULES || + num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n", i); kfree(tb); @@ -4608,20 +5723,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, memcpy(reg_info->alpha2, &ev->alpha2, REG_ALPHA2_LEN); - /* FIXME: Currently FW includes 6G reg rule also in 5G rule - * list for country US. - * Having same 6G reg rule in 5G and 6G rules list causes - * intersect check to be true, and same rules will be shown - * multiple times in iw cmd. So added hack below to avoid - * parsing 6G rule from 5G reg rule list, and this can be - * removed later, after FW updates to remove 6G reg rule - * from 5G rules list. - */ - if (memcmp(reg_info->alpha2, "US", 2) == 0) { - reg_info->num_5g_reg_rules = REG_US_5G_NUM_REG_RULES; - num_5g_reg_rules = reg_info->num_5g_reg_rules; - } - reg_info->dfs_region = le32_to_cpu(ev->dfs_region); reg_info->phybitmap = le32_to_cpu(ev->phybitmap); reg_info->num_phy = le32_to_cpu(ev->num_phy); @@ -4724,8 +5825,29 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, } } + ext_wmi_reg_rule += num_2g_reg_rules; + + /* Firmware might include 6 GHz reg rule in 5 GHz rule list + * for few countries along with separate 6 GHz rule. + * Having same 6 GHz reg rule in 5 GHz and 6 GHz rules list + * causes intersect check to be true, and same rules will be + * shown multiple times in iw cmd. + * Hence, avoid parsing 6 GHz rule from 5 GHz reg rule list + */ + num_invalid_5ghz_ext_rules = ath12k_wmi_ignore_num_extra_rules(ext_wmi_reg_rule, + num_5g_reg_rules); + + if (num_invalid_5ghz_ext_rules) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "CC: %s 5 GHz reg rules number %d from fw, %d number of invalid 5 GHz rules", + reg_info->alpha2, reg_info->num_5g_reg_rules, + num_invalid_5ghz_ext_rules); + + num_5g_reg_rules = num_5g_reg_rules - num_invalid_5ghz_ext_rules; + reg_info->num_5g_reg_rules = num_5g_reg_rules; + } + if (num_5g_reg_rules) { - ext_wmi_reg_rule += num_2g_reg_rules; reg_info->reg_rules_5g_ptr = create_ext_reg_rules_from_wmi(num_5g_reg_rules, ext_wmi_reg_rule); @@ -4737,7 +5859,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, } } - ext_wmi_reg_rule += num_5g_reg_rules; + /* We have adjusted the number of 5 GHz reg rules above. But still those + * many rules needs to be adjusted in ext_wmi_reg_rule. + * + * NOTE: num_invalid_5ghz_ext_rules will be 0 for rest other cases. + */ + ext_wmi_reg_rule += (num_5g_reg_rules + num_invalid_5ghz_ext_rules); for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { reg_info->reg_rules_6g_ap_ptr[i] = @@ -5016,6 +6143,9 @@ static int wmi_process_mgmt_tx_comp(struct ath12k *ar, u32 desc_id, if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) info->flags |= IEEE80211_TX_STAT_ACK; + if ((info->flags & IEEE80211_TX_CTL_NO_ACK) && !status) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + ieee80211_tx_status_irqsafe(ath12k_ar_to_hw(ar), msdu); num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); @@ -5493,30 +6623,62 @@ static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab) wake_up(&ab->wmi_ab.tx_credits_wq); } -static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab, - struct sk_buff *skb) +static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *skb) { - dev_kfree_skb(skb); -} + const struct wmi_11d_new_cc_event *ev; + struct ath12k *ar; + struct ath12k_pdev *pdev; + const void **tb; + int ret, i; -static bool ath12k_reg_is_world_alpha(char *alpha) -{ - if (alpha[0] == '0' && alpha[1] == '0') - return true; + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } - if (alpha[0] == 'n' && alpha[1] == 'a') - return true; + ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; + if (!ev) { + kfree(tb); + ath12k_warn(ab, "failed to fetch 11d new cc ev"); + return -EPROTO; + } - return false; + spin_lock_bh(&ab->base_lock); + memcpy(&ab->new_alpha2, &ev->new_alpha2, REG_ALPHA2_LEN); + spin_unlock_bh(&ab->base_lock); + + ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi 11d new cc %c%c\n", + ab->new_alpha2[0], + ab->new_alpha2[1]); + + kfree(tb); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + ar->state_11d = ATH12K_11D_IDLE; + ar->ah->regd_updated = false; + complete(&ar->completed_11d_scan); + } + + queue_work(ab->workqueue, &ab->update_11d_work); + + return 0; +} + +static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb(skb); } static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *skb) { - struct ath12k_reg_info *reg_info = NULL; - struct ieee80211_regdomain *regd = NULL; - bool intersect = false; - int ret = 0, pdev_idx, i, j; - struct ath12k *ar; + struct ath12k_reg_info *reg_info; + u8 pdev_idx; + int ret; reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); if (!reg_info) { @@ -5525,86 +6687,52 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk } ret = ath12k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info); - if (ret) { ath12k_warn(ab, "failed to extract regulatory info from received event\n"); - goto fallback; + goto mem_free; } - if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { - /* In case of failure to set the requested ctry, - * fw retains the current regd. We print a failure info - * and return from here. + ret = ath12k_reg_validate_reg_info(ab, reg_info); + if (ret == ATH12K_REG_STATUS_FALLBACK) { + ath12k_warn(ab, "failed to validate reg info %d\n", ret); + /* firmware has successfully switches to new regd but host can not + * continue, so free reginfo and fallback to old regd */ - ath12k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + goto mem_free; + } else if (ret == ATH12K_REG_STATUS_DROP) { + /* reg info is valid but we will not store it and + * not going to create new regd for it + */ + ret = ATH12K_REG_STATUS_VALID; goto mem_free; } + /* free old reg_info if it exist */ pdev_idx = reg_info->phy_id; - - if (pdev_idx >= ab->num_radios) { - /* Process the event for phy0 only if single_pdev_only - * is true. If pdev_idx is valid but not 0, discard the - * event. Otherwise, it goes to fallback. - */ - if (ab->hw_params->single_pdev_only && - pdev_idx < ab->hw_params->num_rxmda_per_pdev) - goto mem_free; - else - goto fallback; + if (ab->reg_info[pdev_idx]) { + ath12k_reg_reset_reg_info(ab->reg_info[pdev_idx]); + kfree(ab->reg_info[pdev_idx]); } - - /* Avoid multiple overwrites to default regd, during core - * stop-start after mac registration. + /* reg_info is valid, we store it for later use + * even below regd build failed */ - if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && - !memcmp(ab->default_regd[pdev_idx]->alpha2, - reg_info->alpha2, 2)) - goto mem_free; + ab->reg_info[pdev_idx] = reg_info; - /* Intersect new rules with default regd if a new country setting was - * requested, i.e a default regd was already set during initialization - * and the regd coming from this event has a valid country info. - */ - if (ab->default_regd[pdev_idx] && - !ath12k_reg_is_world_alpha((char *) - ab->default_regd[pdev_idx]->alpha2) && - !ath12k_reg_is_world_alpha((char *)reg_info->alpha2)) - intersect = true; - - regd = ath12k_reg_build_regd(ab, reg_info, intersect); - if (!regd) { - ath12k_warn(ab, "failed to build regd from reg_info\n"); + ret = ath12k_reg_handle_chan_list(ab, reg_info, WMI_VDEV_TYPE_UNSPEC, + IEEE80211_REG_UNSET_AP); + if (ret) { + ath12k_warn(ab, "failed to handle chan list %d\n", ret); goto fallback; } - spin_lock(&ab->base_lock); - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { - /* Once mac is registered, ar is valid and all CC events from - * fw is considered to be received due to user requests - * currently. - * Free previously built regd before assigning the newly - * generated regd to ar. NULL pointer handling will be - * taken care by kfree itself. - */ - ar = ab->pdevs[pdev_idx].ar; - kfree(ab->new_regd[pdev_idx]); - ab->new_regd[pdev_idx] = regd; - queue_work(ab->workqueue, &ar->regd_update_work); - } else { - /* Multiple events for the same *ar is not expected. But we - * can still clear any previously stored default_regd if we - * are receiving this event for the same radio by mistake. - * NULL pointer handling will be taken care by kfree itself. - */ - kfree(ab->default_regd[pdev_idx]); - /* This regd would be applied during mac registration */ - ab->default_regd[pdev_idx] = regd; - } - ab->dfs_region = reg_info->dfs_region; - spin_unlock(&ab->base_lock); + goto out; + +mem_free: + ath12k_reg_reset_reg_info(reg_info); + kfree(reg_info); - goto mem_free; + if (ret == ATH12K_REG_STATUS_VALID) + return ret; fallback: /* Fallback to older reg (by sending previous country setting @@ -5616,20 +6744,8 @@ fallback: */ /* TODO: This is rare, but still should also be handled */ WARN_ON(1); -mem_free: - if (reg_info) { - kfree(reg_info->reg_rules_2g_ptr); - kfree(reg_info->reg_rules_5g_ptr); - if (reg_info->is_ext_reg_event) { - for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) - kfree(reg_info->reg_rules_6g_ap_ptr[i]); - - for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) - for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) - kfree(reg_info->reg_rules_6g_client_ptr[j][i]); - } - kfree(reg_info); - } + +out: return ret; } @@ -5785,13 +6901,14 @@ static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff ar->last_wmi_vdev_start_status = 0; status = le32_to_cpu(vdev_start_resp.status); - if (WARN_ON_ONCE(status)) { ath12k_warn(ab, "vdev start resp error status %d (%s)\n", status, ath12k_wmi_vdev_resp_print(status)); ar->last_wmi_vdev_start_status = status; } + ar->max_allowed_tx_power = (s8)le32_to_cpu(vdev_start_resp.max_allowed_tx_power); + complete(&ar->vdev_setup_done); rcu_read_unlock(); @@ -5866,7 +6983,7 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) goto exit; } - if ((test_bit(ATH12K_CAC_RUNNING, &ar->dev_flags)) || + if ((test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) || (rx_ev.status & (WMI_RX_STATUS_ERR_DECRYPT | WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) { @@ -5877,11 +6994,13 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) if (rx_ev.status & WMI_RX_STATUS_ERR_MIC) status->flag |= RX_FLAG_MMIC_ERROR; - if (rx_ev.chan_freq >= ATH12K_MIN_6G_FREQ) { + if (rx_ev.chan_freq >= ATH12K_MIN_6GHZ_FREQ && + rx_ev.chan_freq <= ATH12K_MAX_6GHZ_FREQ) { status->band = NL80211_BAND_6GHZ; + status->freq = rx_ev.chan_freq; } else if (rx_ev.channel >= 1 && rx_ev.channel <= 14) { status->band = NL80211_BAND_2GHZ; - } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH12K_MAX_5G_CHAN) { + } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH12K_MAX_5GHZ_CHAN) { status->band = NL80211_BAND_5GHZ; } else { /* Shouldn't happen unless list of advertised channels to @@ -5899,8 +7018,10 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) sband = &ar->mac.sbands[status->band]; - status->freq = ieee80211_channel_to_frequency(rx_ev.channel, - status->band); + if (status->band != NL80211_BAND_6GHZ) + status->freq = ieee80211_channel_to_frequency(rx_ev.channel, + status->band); + status->signal = rx_ev.snr + ATH12K_DEFAULT_NOISE_FLOOR; status->rate_idx = ath12k_mac_bitrate_to_idx(sband, rx_ev.rate / 100); @@ -5927,13 +7048,11 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) } } - /* TODO: Pending handle beacon implementation - *if (ieee80211_is_beacon(hdr->frame_control)) - * ath12k_mac_handle_beacon(ar, skb); - */ + if (ieee80211_is_beacon(hdr->frame_control)) + ath12k_mac_handle_beacon(ar, skb); ath12k_dbg(ab, ATH12K_DBG_MGMT, - "event mgmt rx skb %pK len %d ftype %02x stype %02x\n", + "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); @@ -5993,7 +7112,8 @@ static struct ath12k *ath12k_get_ar_on_scan_state(struct ath12k_base *ab, spin_lock_bh(&ar->data_lock); if (ar->scan.state == state && - ar->scan.vdev_id == vdev_id) { + ar->scan.arvif && + ar->scan.arvif->vdev_id == vdev_id) { spin_unlock_bh(&ar->data_lock); return ar; } @@ -6137,42 +7257,44 @@ static void ath12k_roam_event(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_roam_event roam_ev = {}; struct ath12k *ar; + u32 vdev_id; + u8 roam_reason; if (ath12k_pull_roam_ev(ab, skb, &roam_ev) != 0) { ath12k_warn(ab, "failed to extract roam event"); return; } + vdev_id = le32_to_cpu(roam_ev.vdev_id); + roam_reason = u32_get_bits(le32_to_cpu(roam_ev.reason), + WMI_ROAM_REASON_MASK); + ath12k_dbg(ab, ATH12K_DBG_WMI, - "wmi roam event vdev %u reason 0x%08x rssi %d\n", - roam_ev.vdev_id, roam_ev.reason, roam_ev.rssi); + "wmi roam event vdev %u reason %d rssi %d\n", + vdev_id, roam_reason, roam_ev.rssi); rcu_read_lock(); - ar = ath12k_mac_get_ar_by_vdev_id(ab, le32_to_cpu(roam_ev.vdev_id)); + ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id); if (!ar) { - ath12k_warn(ab, "invalid vdev id in roam ev %d", - roam_ev.vdev_id); + ath12k_warn(ab, "invalid vdev id in roam ev %d", vdev_id); rcu_read_unlock(); return; } - if (le32_to_cpu(roam_ev.reason) >= WMI_ROAM_REASON_MAX) + if (roam_reason >= WMI_ROAM_REASON_MAX) ath12k_warn(ab, "ignoring unknown roam event reason %d on vdev %i\n", - roam_ev.reason, roam_ev.vdev_id); + roam_reason, vdev_id); - switch (le32_to_cpu(roam_ev.reason)) { + switch (roam_reason) { case WMI_ROAM_REASON_BEACON_MISS: - /* TODO: Pending beacon miss and connection_loss_work - * implementation - * ath12k_mac_handle_beacon_miss(ar, vdev_id); - */ + ath12k_mac_handle_beacon_miss(ar, vdev_id); break; case WMI_ROAM_REASON_BETTER_AP: case WMI_ROAM_REASON_LOW_RSSI: case WMI_ROAM_REASON_SUITABLE_AP_FOUND: case WMI_ROAM_REASON_HO_FAILED: ath12k_warn(ab, "ignoring not implemented roam event reason %d on vdev %i\n", - roam_ev.reason, roam_ev.vdev_id); + roam_reason, vdev_id); break; } @@ -6459,8 +7581,694 @@ static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff rcu_read_unlock(); } +static void +ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_vdev *vdev; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + struct ath12k_link_vif *arvif; + u32 len = *length; + u8 *vif_macaddr; + int i; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath12k VDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) { + arvif = ath12k_mac_get_arvif(ar, vdev->vdev_id); + if (!arvif) + continue; + vif_macaddr = arvif->ahvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", vdev->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vif_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "beacon snr", vdev->beacon_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "data snr", vdev->data_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx frames", vdev->num_rx_frames); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts fail", vdev->num_rts_fail); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts success", vdev->num_rts_success); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx err", vdev->num_rx_err); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx discard", vdev->num_rx_discard); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num tx not acked", vdev->num_tx_not_acked); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames", i, + vdev->num_tx_frames[i]); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames retries", i, + vdev->num_tx_frames_retries[i]); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames failures", i, + vdev->num_tx_frames_failures[i]); + + for (i = 0 ; i < MAX_TX_RATE_VALUES; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] 0x%08x\n", + "tx rate history", i, + vdev->tx_rate_history[i]); + for (i = 0 ; i < MAX_TX_RATE_VALUES; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "beacon rssi history", i, + vdev->beacon_rssi_history[i]); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; + } +} + +static void +ath12k_wmi_fw_bcn_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_bcn *bcn; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + struct ath12k_link_vif *arvif; + u32 len = *length; + size_t num_bcn; + + num_bcn = list_count_nodes(&fw_stats->bcn); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath12k Beacon stats", num_bcn); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "==================="); + + list_for_each_entry(bcn, &fw_stats->bcn, list) { + arvif = ath12k_mac_get_arvif(ar, bcn->vdev_id); + if (!arvif) + continue; + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", bcn->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", arvif->ahvif->vif->addr); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================"); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx success", bcn->tx_bcn_succ_cnt); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx failures", bcn->tx_bcn_outage_cnt); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; + } +} + +static void +ath12k_wmi_fw_pdev_base_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length, u64 fw_soc_drop_cnt) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len = scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath12k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", pdev->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", pdev->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", pdev->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", pdev->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", pdev->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", pdev->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", pdev->phy_err_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10llu\n", + "soc drop count", fw_soc_drop_cnt); + + *length = len; +} + +static void +ath12k_wmi_fw_pdev_tx_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath12k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", pdev->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", pdev->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", pdev->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", pdev->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", pdev->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", pdev->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", pdev->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", pdev->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", pdev->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requeued", pdev->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Excessive retries", pdev->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Sched self triggers", pdev->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Dropped due to SW retries", + pdev->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Illegal rate phy errors", + pdev->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV continuous xretry", pdev->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX timeout", pdev->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV resets", pdev->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Stateless TIDs alloc failures", + pdev->stateless_tid_alloc_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MPDU is more than txop limit", pdev->txop_ovf); + *length = len; +} + +static void +ath12k_wmi_fw_pdev_rx_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath12k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + pdev->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", pdev->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", pdev->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", pdev->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", pdev->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", pdev->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", pdev->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", pdev->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", pdev->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", pdev->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", pdev->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", pdev->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); + *length = len; +} + +static void +ath12k_wmi_fw_pdev_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_pdev *pdev; + u32 len = *length; + + pdev = list_first_entry_or_null(&fw_stats->pdevs, + struct ath12k_fw_stats_pdev, list); + if (!pdev) { + ath12k_warn(ar->ab, "failed to get pdev stats\n"); + return; + } + + ath12k_wmi_fw_pdev_base_stats_dump(pdev, buf, &len, + ar->ab->fw_soc_drop_count); + ath12k_wmi_fw_pdev_tx_stats_dump(pdev, buf, &len); + ath12k_wmi_fw_pdev_rx_stats_dump(pdev, buf, &len); + + *length = len; +} + +void ath12k_wmi_fw_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + u32 stats_id, char *buf) +{ + u32 len = 0; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + spin_lock_bh(&ar->data_lock); + + switch (stats_id) { + case WMI_REQUEST_VDEV_STAT: + ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len); + break; + case WMI_REQUEST_BCN_STAT: + ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len); + break; + case WMI_REQUEST_PDEV_STAT: + ath12k_wmi_fw_pdev_stats_dump(ar, fw_stats, buf, &len); + break; + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; + + ath12k_fw_stats_reset(ar); +} + +static void +ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src, + struct ath12k_fw_stats_vdev *dst) +{ + int i; + + dst->vdev_id = le32_to_cpu(src->vdev_id); + dst->beacon_snr = le32_to_cpu(src->beacon_snr); + dst->data_snr = le32_to_cpu(src->data_snr); + dst->num_rx_frames = le32_to_cpu(src->num_rx_frames); + dst->num_rts_fail = le32_to_cpu(src->num_rts_fail); + dst->num_rts_success = le32_to_cpu(src->num_rts_success); + dst->num_rx_err = le32_to_cpu(src->num_rx_err); + dst->num_rx_discard = le32_to_cpu(src->num_rx_discard); + dst->num_tx_not_acked = le32_to_cpu(src->num_tx_not_acked); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames[i] = + le32_to_cpu(src->num_tx_frames[i]); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames_retries[i] = + le32_to_cpu(src->num_tx_frames_retries[i]); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames_failures[i] = + le32_to_cpu(src->num_tx_frames_failures[i]); + + for (i = 0; i < MAX_TX_RATE_VALUES; i++) + dst->tx_rate_history[i] = + le32_to_cpu(src->tx_rate_history[i]); + + for (i = 0; i < MAX_TX_RATE_VALUES; i++) + dst->beacon_rssi_history[i] = + le32_to_cpu(src->beacon_rssi_history[i]); +} + +static void +ath12k_wmi_pull_bcn_stats(const struct ath12k_wmi_bcn_stats_params *src, + struct ath12k_fw_stats_bcn *dst) +{ + dst->vdev_id = le32_to_cpu(src->vdev_id); + dst->tx_bcn_succ_cnt = le32_to_cpu(src->tx_bcn_succ_cnt); + dst->tx_bcn_outage_cnt = le32_to_cpu(src->tx_bcn_outage_cnt); +} + +static void +ath12k_wmi_pull_pdev_stats_base(const struct ath12k_wmi_pdev_base_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->ch_noise_floor = a_sle32_to_cpu(src->chan_nf); + dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count); + dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count); + dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count); + dst->cycle_count = __le32_to_cpu(src->cycle_count); + dst->phy_err_count = __le32_to_cpu(src->phy_err_count); + dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr); +} + +static void +ath12k_wmi_pull_pdev_stats_tx(const struct ath12k_wmi_pdev_tx_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->comp_queued = a_sle32_to_cpu(src->comp_queued); + dst->comp_delivered = a_sle32_to_cpu(src->comp_delivered); + dst->msdu_enqued = a_sle32_to_cpu(src->msdu_enqued); + dst->mpdu_enqued = a_sle32_to_cpu(src->mpdu_enqued); + dst->wmm_drop = a_sle32_to_cpu(src->wmm_drop); + dst->local_enqued = a_sle32_to_cpu(src->local_enqued); + dst->local_freed = a_sle32_to_cpu(src->local_freed); + dst->hw_queued = a_sle32_to_cpu(src->hw_queued); + dst->hw_reaped = a_sle32_to_cpu(src->hw_reaped); + dst->underrun = a_sle32_to_cpu(src->underrun); + dst->tx_abort = a_sle32_to_cpu(src->tx_abort); + dst->mpdus_requed = a_sle32_to_cpu(src->mpdus_requed); + dst->tx_ko = __le32_to_cpu(src->tx_ko); + dst->data_rc = __le32_to_cpu(src->data_rc); + dst->self_triggers = __le32_to_cpu(src->self_triggers); + dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure); + dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err); + dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry); + dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout); + dst->pdev_resets = __le32_to_cpu(src->pdev_resets); + dst->stateless_tid_alloc_failure = + __le32_to_cpu(src->stateless_tid_alloc_failure); + dst->phy_underrun = __le32_to_cpu(src->phy_underrun); + dst->txop_ovf = __le32_to_cpu(src->txop_ovf); +} + +static void +ath12k_wmi_pull_pdev_stats_rx(const struct ath12k_wmi_pdev_rx_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->mid_ppdu_route_change = + a_sle32_to_cpu(src->mid_ppdu_route_change); + dst->status_rcvd = a_sle32_to_cpu(src->status_rcvd); + dst->r0_frags = a_sle32_to_cpu(src->r0_frags); + dst->r1_frags = a_sle32_to_cpu(src->r1_frags); + dst->r2_frags = a_sle32_to_cpu(src->r2_frags); + dst->r3_frags = a_sle32_to_cpu(src->r3_frags); + dst->htt_msdus = a_sle32_to_cpu(src->htt_msdus); + dst->htt_mpdus = a_sle32_to_cpu(src->htt_mpdus); + dst->loc_msdus = a_sle32_to_cpu(src->loc_msdus); + dst->loc_mpdus = a_sle32_to_cpu(src->loc_mpdus); + dst->oversize_amsdu = a_sle32_to_cpu(src->oversize_amsdu); + dst->phy_errs = a_sle32_to_cpu(src->phy_errs); + dst->phy_err_drop = a_sle32_to_cpu(src->phy_err_drop); + dst->mpdu_errs = a_sle32_to_cpu(src->mpdu_errs); +} + +static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, + struct wmi_tlv_fw_stats_parse *parse, + const void *ptr, + u16 len) +{ + const struct wmi_stats_event *ev = parse->ev; + struct ath12k_fw_stats *stats = parse->stats; + struct ath12k *ar; + struct ath12k_link_vif *arvif; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + int i, ret = 0; + const void *data = ptr; + + if (!ev) { + ath12k_warn(ab, "failed to fetch update stats ev"); + return -EPROTO; + } + + if (!stats) + return -EINVAL; + + rcu_read_lock(); + + stats->pdev_id = le32_to_cpu(ev->pdev_id); + ar = ath12k_mac_get_ar_by_pdev_id(ab, stats->pdev_id); + if (!ar) { + ath12k_warn(ab, "invalid pdev id %d in update stats event\n", + le32_to_cpu(ev->pdev_id)); + ret = -EPROTO; + goto exit; + } + + for (i = 0; i < le32_to_cpu(ev->num_vdev_stats); i++) { + const struct wmi_vdev_stats_params *src; + struct ath12k_fw_stats_vdev *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + arvif = ath12k_mac_get_arvif(ar, le32_to_cpu(src->vdev_id)); + if (arvif) { + sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), + arvif->bssid, + NULL); + if (sta) { + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + arsta->rssi_beacon = le32_to_cpu(src->beacon_snr); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "wmi stats vdev id %d snr %d\n", + src->vdev_id, src->beacon_snr); + } else { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "not found station bssid %pM for vdev stat\n", + arvif->bssid); + } + } + + data += sizeof(*src); + len -= sizeof(*src); + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + ath12k_wmi_pull_vdev_stats(src, dst); + stats->stats_id = WMI_REQUEST_VDEV_STAT; + list_add_tail(&dst->list, &stats->vdevs); + } + for (i = 0; i < le32_to_cpu(ev->num_bcn_stats); i++) { + const struct ath12k_wmi_bcn_stats_params *src; + struct ath12k_fw_stats_bcn *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + data += sizeof(*src); + len -= sizeof(*src); + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + ath12k_wmi_pull_bcn_stats(src, dst); + stats->stats_id = WMI_REQUEST_BCN_STAT; + list_add_tail(&dst->list, &stats->bcn); + } + for (i = 0; i < le32_to_cpu(ev->num_pdev_stats); i++) { + const struct ath12k_wmi_pdev_stats_params *src; + struct ath12k_fw_stats_pdev *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + stats->stats_id = WMI_REQUEST_PDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath12k_wmi_pull_pdev_stats_base(&src->base, dst); + ath12k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath12k_wmi_pull_pdev_stats_rx(&src->rx, dst); + list_add_tail(&dst->list, &stats->pdevs); + } + +exit: + rcu_read_unlock(); + return ret; +} + +static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_fw_stats_parse *parse = data; + int ret = 0; + + switch (tag) { + case WMI_TAG_STATS_EVENT: + parse->ev = ptr; + break; + case WMI_TAG_ARRAY_BYTE: + ret = ath12k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); + break; + default: + break; + } + return ret; +} + +static int ath12k_wmi_pull_fw_stats(struct ath12k_base *ab, struct sk_buff *skb, + struct ath12k_fw_stats *stats) +{ + struct wmi_tlv_fw_stats_parse parse = {}; + + stats->stats_id = 0; + parse.stats = stats; + + return ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tlv_fw_stats_parse, + &parse); +} + +static void ath12k_wmi_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_pdev *pdev; + bool is_end = true; + size_t total_vdevs_started = 0; + int i; + + if (stats->stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats->vdevs)) { + ath12k_warn(ab, "empty vdev stats"); + return; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + rcu_read_lock(); + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += pdev->ar->num_started_vdevs; + } + rcu_read_unlock(); + + if (total_vdevs_started) + is_end = ((++ar->fw_stats.num_vdev_recvd) == + total_vdevs_started); + + list_splice_tail_init(&stats->vdevs, + &ar->fw_stats.vdevs); + + if (is_end) + complete(&ar->fw_stats_done); + + return; + } + + if (stats->stats_id == WMI_REQUEST_BCN_STAT) { + if (list_empty(&stats->bcn)) { + ath12k_warn(ab, "empty beacon stats"); + return; + } + /* Mark end until we reached the count of all started VDEVs + * within the PDEV + */ + if (ar->num_started_vdevs) + is_end = ((++ar->fw_stats.num_bcn_recvd) == + ar->num_started_vdevs); + + list_splice_tail_init(&stats->bcn, + &ar->fw_stats.bcn); + + if (is_end) + complete(&ar->fw_stats_done); + } +} + static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb) { + struct ath12k_fw_stats stats = {}; + struct ath12k *ar; + int ret; + + INIT_LIST_HEAD(&stats.pdevs); + INIT_LIST_HEAD(&stats.vdevs); + INIT_LIST_HEAD(&stats.bcn); + + ret = ath12k_wmi_pull_fw_stats(ab, skb, &stats); + if (ret) { + ath12k_warn(ab, "failed to pull fw stats: %d\n", ret); + goto free; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, "event update stats"); + + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, stats.pdev_id); + if (!ar) { + rcu_read_unlock(); + ath12k_warn(ab, "failed to get ar for pdev_id %d: %d\n", + stats.pdev_id, ret); + goto free; + } + + spin_lock_bh(&ar->data_lock); + + /* Handle WMI_REQUEST_PDEV_STAT status update */ + if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { + list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs); + complete(&ar->fw_stats_done); + goto complete; + } + + /* Handle WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT updates. */ + ath12k_wmi_fw_stats_process(ar, &stats); + +complete: + complete(&ar->fw_stats_complete); + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); + + /* Since the stats's pdev, vdev and beacon list are spliced and reinitialised + * at this point, no need to free the individual list. + */ + return; + +free: + ath12k_fw_stats_free(&stats); } /* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned @@ -6506,15 +8314,15 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, const struct ath12k_wmi_pdev_csa_event *ev, const u32 *vdev_ids) { + u32 current_switch_count = le32_to_cpu(ev->current_switch_count); + u32 num_vdevs = le32_to_cpu(ev->num_vdevs); + struct ieee80211_bss_conf *conf; + struct ath12k_link_vif *arvif; + struct ath12k_vif *ahvif; int i; - struct ath12k_vif *arvif; - - /* Finish CSA once the switch count becomes NULL */ - if (ev->current_switch_count) - return; rcu_read_lock(); - for (i = 0; i < le32_to_cpu(ev->num_vdevs); i++) { + for (i = 0; i < num_vdevs; i++) { arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); if (!arvif) { @@ -6522,9 +8330,41 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, vdev_ids[i]); continue; } + ahvif = arvif->ahvif; + + if (arvif->link_id >= IEEE80211_MLD_MAX_NUM_LINKS) { + ath12k_warn(ab, "Invalid CSA switch count even link id: %d\n", + arvif->link_id); + continue; + } + + conf = rcu_dereference(ahvif->vif->link_conf[arvif->link_id]); + if (!conf) { + ath12k_warn(ab, "unable to access bss link conf in process csa for vif %pM link %u\n", + ahvif->vif->addr, arvif->link_id); + continue; + } + + if (!arvif->is_up || !conf->csa_active) + continue; + + /* Finish CSA when counter reaches zero */ + if (!current_switch_count) { + ieee80211_csa_finish(ahvif->vif, arvif->link_id); + arvif->current_cntdown_counter = 0; + } else if (current_switch_count > 1) { + /* If the count in event is not what we expect, don't update the + * mac80211 count. Since during beacon Tx failure, count in the + * firmware will not decrement and this event will come with the + * previous count value again + */ + if (current_switch_count != arvif->current_cntdown_counter) + continue; - if (arvif->is_up && arvif->vif->bss_conf.csa_active) - ieee80211_csa_finish(arvif->vif, 0); + arvif->current_cntdown_counter = + ieee80211_beacon_update_cntdwn(ahvif->vif, + arvif->link_id); + } } rcu_read_unlock(); } @@ -6568,6 +8408,7 @@ static void ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff *skb) { const void **tb; + struct ath12k_mac_get_any_chanctx_conf_arg arg; const struct ath12k_wmi_pdev_radar_event *ev; struct ath12k *ar; int ret; @@ -6603,13 +8444,22 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff goto exit; } + arg.ar = ar; + arg.chanctx_conf = NULL; + ieee80211_iter_chan_contexts_atomic(ath12k_ar_to_hw(ar), + ath12k_mac_get_any_chanctx_conf_iter, &arg); + if (!arg.chanctx_conf) { + ath12k_warn(ab, "failed to find valid chanctx_conf in radar detected event\n"); + goto exit; + } + ath12k_dbg(ar->ab, ATH12K_DBG_REG, "DFS Radar Detected in pdev %d\n", ev->pdev_id); if (ar->dfs_block_radar_events) ath12k_info(ab, "DFS Radar detected, but ignored as requested\n"); else - ieee80211_radar_detected(ath12k_ar_to_hw(ar)); + ieee80211_radar_detected(ath12k_ar_to_hw(ar), arg.chanctx_conf); exit: rcu_read_unlock(); @@ -6617,6 +8467,35 @@ exit: kfree(tb); } +static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + const struct ath12k_wmi_ftm_event *ev; + const void **tb; + int ret; + u16 length; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse ftm event tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_ARRAY_BYTE]; + if (!ev) { + ath12k_warn(ab, "failed to fetch ftm msg\n"); + kfree(tb); + return; + } + + length = skb->len - TLV_HDR_SIZE; + ath12k_tm_process_event(ab, cmd_id, ev, length); + kfree(tb); + tb = NULL; +} + static void ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, struct sk_buff *skb) @@ -6854,6 +8733,572 @@ exit: kfree(tb); } +static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + const struct wmi_wow_ev_pg_fault_param *pf_param; + const struct wmi_wow_ev_param *param; + struct wmi_wow_ev_arg *arg = data; + int pf_len; + + switch (tag) { + case WMI_TAG_WOW_EVENT_INFO: + param = ptr; + arg->wake_reason = le32_to_cpu(param->wake_reason); + ath12k_dbg(ab, ATH12K_DBG_WMI, "wow wakeup host reason %d %s\n", + arg->wake_reason, wow_reason(arg->wake_reason)); + break; + + case WMI_TAG_ARRAY_BYTE: + if (arg && arg->wake_reason == WOW_REASON_PAGE_FAULT) { + pf_param = ptr; + pf_len = le32_to_cpu(pf_param->len); + if (pf_len > len - sizeof(pf_len) || + pf_len < 0) { + ath12k_warn(ab, "invalid wo reason page fault buffer len %d\n", + pf_len); + return -EINVAL; + } + ath12k_dbg(ab, ATH12K_DBG_WMI, "wow_reason_page_fault len %d\n", + pf_len); + ath12k_dbg_dump(ab, ATH12K_DBG_WMI, + "wow_reason_page_fault packet present", + "wow_pg_fault ", + pf_param->data, + pf_len); + } + break; + default: + break; + } + + return 0; +} + +static void ath12k_wmi_event_wow_wakeup_host(struct ath12k_base *ab, struct sk_buff *skb) +{ + struct wmi_wow_ev_arg arg = { }; + int ret; + + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_wow_wakeup_host_parse, + &arg); + if (ret) { + ath12k_warn(ab, "failed to parse wmi wow wakeup host event tlv: %d\n", + ret); + return; + } + + complete(&ab->wow.wakeup_completed); +} + +static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_gtk_offload_status_event *ev; + struct ath12k_link_vif *arvif; + __be64 replay_ctr_be; + u64 replay_ctr; + const void **tb; + int ret; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT]; + if (!ev) { + ath12k_warn(ab, "failed to fetch gtk offload status ev"); + kfree(tb); + return; + } + + rcu_read_lock(); + arvif = ath12k_mac_get_arvif_by_vdev_id(ab, le32_to_cpu(ev->vdev_id)); + if (!arvif) { + rcu_read_unlock(); + ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n", + le32_to_cpu(ev->vdev_id)); + kfree(tb); + return; + } + + replay_ctr = le64_to_cpu(ev->replay_ctr); + arvif->rekey_data.replay_ctr = replay_ctr; + ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi gtk offload event refresh_cnt %d replay_ctr %llu\n", + le32_to_cpu(ev->refresh_cnt), replay_ctr); + + /* supplicant expects big-endian replay counter */ + replay_ctr_be = cpu_to_be64(replay_ctr); + + ieee80211_gtk_rekey_notify(arvif->ahvif->vif, arvif->bssid, + (void *)&replay_ctr_be, GFP_ATOMIC); + + rcu_read_unlock(); + + kfree(tb); +} + +static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_mlo_setup_complete_event *ev; + struct ath12k *ar = NULL; + struct ath12k_pdev *pdev; + const void **tb; + int ret, i; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse mlo setup complete event tlv: %d\n", + ret); + return; + } + + ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT]; + if (!ev) { + ath12k_warn(ab, "failed to fetch mlo setup complete event\n"); + kfree(tb); + return; + } + + if (le32_to_cpu(ev->pdev_id) > ab->num_radios) + goto skip_lookup; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + if (pdev && pdev->pdev_id == le32_to_cpu(ev->pdev_id)) { + ar = pdev->ar; + break; + } + } + +skip_lookup: + if (!ar) { + ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n", + ev->pdev_id, ev->status); + goto out; + } + + ar->mlo_setup_status = le32_to_cpu(ev->status); + complete(&ar->mlo_setup_done); + +out: + kfree(tb); +} + +static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_mlo_teardown_complete_event *ev; + const void **tb; + int ret; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE]; + if (!ev) { + ath12k_warn(ab, "failed to fetch teardown complete event\n"); + kfree(tb); + return; + } + + kfree(tb); +} + +#ifdef CONFIG_ATH12K_DEBUGFS +static int ath12k_wmi_tpc_stats_copy_buffer(struct ath12k_base *ab, + const void *ptr, u16 tag, u16 len, + struct wmi_tpc_stats_arg *tpc_stats) +{ + u32 len1, len2, len3, len4; + s16 *dst_ptr; + s8 *dst_ptr_ctl; + + len1 = le32_to_cpu(tpc_stats->max_reg_allowed_power.tpc_reg_pwr.reg_array_len); + len2 = le32_to_cpu(tpc_stats->rates_array1.tpc_rates_array.rate_array_len); + len3 = le32_to_cpu(tpc_stats->rates_array2.tpc_rates_array.rate_array_len); + len4 = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.ctl_array_len); + + switch (tpc_stats->event_count) { + case ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT: + if (len1 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_REG_PWR_ALLOWED) { + dst_ptr = tpc_stats->max_reg_allowed_power.reg_pwr_array; + memcpy(dst_ptr, ptr, len1); + } + break; + case ATH12K_TPC_STATS_RATES_EVENT1: + if (len2 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY1) { + dst_ptr = tpc_stats->rates_array1.rate_array; + memcpy(dst_ptr, ptr, len2); + } + break; + case ATH12K_TPC_STATS_RATES_EVENT2: + if (len3 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY2) { + dst_ptr = tpc_stats->rates_array2.rate_array; + memcpy(dst_ptr, ptr, len3); + } + break; + case ATH12K_TPC_STATS_CTL_TABLE_EVENT: + if (len4 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) { + dst_ptr_ctl = tpc_stats->ctl_array.ctl_pwr_table; + memcpy(dst_ptr_ctl, ptr, len4); + } + break; + } + return 0; +} + +static int ath12k_tpc_get_reg_pwr(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_max_reg_power_fixed_params *ev) +{ + struct wmi_max_reg_power_allowed_arg *reg_pwr; + u32 total_size; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received reg power array type %d length %d for tpc stats\n", + ev->reg_power_type, ev->reg_array_len); + + switch (le32_to_cpu(ev->reg_power_type)) { + case TPC_STATS_REG_PWR_ALLOWED_TYPE: + reg_pwr = &tpc_stats->max_reg_allowed_power; + break; + default: + return -EINVAL; + } + + /* Each entry is 2 byte hence multiplying the indices with 2 */ + total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) * + le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4) * 2; + if (le32_to_cpu(ev->reg_array_len) != total_size) { + ath12k_warn(ab, + "Total size and reg_array_len doesn't match for tpc stats\n"); + return -EINVAL; + } + + memcpy(®_pwr->tpc_reg_pwr, ev, sizeof(struct wmi_max_reg_power_fixed_params)); + + reg_pwr->reg_pwr_array = kzalloc(le32_to_cpu(reg_pwr->tpc_reg_pwr.reg_array_len), + GFP_ATOMIC); + if (!reg_pwr->reg_pwr_array) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= WMI_TPC_REG_PWR_ALLOWED; + + return 0; +} + +static int ath12k_tpc_get_rate_array(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_tpc_rates_array_fixed_params *ev) +{ + struct wmi_tpc_rates_array_arg *rates_array; + u32 flag = 0, rate_array_len; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received rates array type %d length %d for tpc stats\n", + ev->rate_array_type, ev->rate_array_len); + + switch (le32_to_cpu(ev->rate_array_type)) { + case ATH12K_TPC_STATS_RATES_ARRAY1: + rates_array = &tpc_stats->rates_array1; + flag = WMI_TPC_RATES_ARRAY1; + break; + case ATH12K_TPC_STATS_RATES_ARRAY2: + rates_array = &tpc_stats->rates_array2; + flag = WMI_TPC_RATES_ARRAY2; + break; + default: + ath12k_warn(ab, + "Received invalid type of rates array for tpc stats\n"); + return -EINVAL; + } + memcpy(&rates_array->tpc_rates_array, ev, + sizeof(struct wmi_tpc_rates_array_fixed_params)); + rate_array_len = le32_to_cpu(rates_array->tpc_rates_array.rate_array_len); + rates_array->rate_array = kzalloc(rate_array_len, GFP_ATOMIC); + if (!rates_array->rate_array) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= flag; + return 0; +} + +static int ath12k_tpc_get_ctl_pwr_tbl(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_tpc_ctl_pwr_fixed_params *ev) +{ + struct wmi_tpc_ctl_pwr_table_arg *ctl_array; + u32 total_size, ctl_array_len, flag = 0; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received ctl array type %d length %d for tpc stats\n", + ev->ctl_array_type, ev->ctl_array_len); + + switch (le32_to_cpu(ev->ctl_array_type)) { + case ATH12K_TPC_STATS_CTL_ARRAY: + ctl_array = &tpc_stats->ctl_array; + flag = WMI_TPC_CTL_PWR_ARRAY; + break; + default: + ath12k_warn(ab, + "Received invalid type of ctl pwr table for tpc stats\n"); + return -EINVAL; + } + + total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) * + le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4); + if (le32_to_cpu(ev->ctl_array_len) != total_size) { + ath12k_warn(ab, + "Total size and ctl_array_len doesn't match for tpc stats\n"); + return -EINVAL; + } + + memcpy(&ctl_array->tpc_ctl_pwr, ev, sizeof(struct wmi_tpc_ctl_pwr_fixed_params)); + ctl_array_len = le32_to_cpu(ctl_array->tpc_ctl_pwr.ctl_array_len); + ctl_array->ctl_pwr_table = kzalloc(ctl_array_len, GFP_ATOMIC); + if (!ctl_array->ctl_pwr_table) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= flag; + return 0; +} + +static int ath12k_wmi_tpc_stats_subtlv_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tpc_rates_array_fixed_params *tpc_rates_array; + struct wmi_max_reg_power_fixed_params *tpc_reg_pwr; + struct wmi_tpc_ctl_pwr_fixed_params *tpc_ctl_pwr; + struct wmi_tpc_stats_arg *tpc_stats = data; + struct wmi_tpc_config_params *tpc_config; + int ret = 0; + + if (!tpc_stats) { + ath12k_warn(ab, "tpc stats memory unavailable\n"); + return -EINVAL; + } + + switch (tag) { + case WMI_TAG_TPC_STATS_CONFIG_EVENT: + tpc_config = (struct wmi_tpc_config_params *)ptr; + memcpy(&tpc_stats->tpc_config, tpc_config, + sizeof(struct wmi_tpc_config_params)); + break; + case WMI_TAG_TPC_STATS_REG_PWR_ALLOWED: + tpc_reg_pwr = (struct wmi_max_reg_power_fixed_params *)ptr; + ret = ath12k_tpc_get_reg_pwr(ab, tpc_stats, tpc_reg_pwr); + break; + case WMI_TAG_TPC_STATS_RATES_ARRAY: + tpc_rates_array = (struct wmi_tpc_rates_array_fixed_params *)ptr; + ret = ath12k_tpc_get_rate_array(ab, tpc_stats, tpc_rates_array); + break; + case WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT: + tpc_ctl_pwr = (struct wmi_tpc_ctl_pwr_fixed_params *)ptr; + ret = ath12k_tpc_get_ctl_pwr_tbl(ab, tpc_stats, tpc_ctl_pwr); + break; + default: + ath12k_warn(ab, + "Received invalid tag for tpc stats in subtlvs\n"); + return -EINVAL; + } + return ret; +} + +static int ath12k_wmi_tpc_stats_event_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tpc_stats_arg *tpc_stats = (struct wmi_tpc_stats_arg *)data; + int ret; + + switch (tag) { + case WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM: + ret = 0; + /* Fixed param is already processed*/ + break; + case WMI_TAG_ARRAY_STRUCT: + /* len 0 is expected for array of struct when there + * is no content of that type to pack inside that tlv + */ + if (len == 0) + return 0; + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_tpc_stats_subtlv_parser, + tpc_stats); + break; + case WMI_TAG_ARRAY_INT16: + if (len == 0) + return 0; + ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr, + WMI_TAG_ARRAY_INT16, + len, tpc_stats); + break; + case WMI_TAG_ARRAY_BYTE: + if (len == 0) + return 0; + ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr, + WMI_TAG_ARRAY_BYTE, + len, tpc_stats); + break; + default: + ath12k_warn(ab, "Received invalid tag for tpc stats\n"); + ret = -EINVAL; + break; + } + return ret; +} + +void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar) +{ + struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats; + + lockdep_assert_held(&ar->data_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc stats mem free\n"); + if (tpc_stats) { + kfree(tpc_stats->max_reg_allowed_power.reg_pwr_array); + kfree(tpc_stats->rates_array1.rate_array); + kfree(tpc_stats->rates_array2.rate_array); + kfree(tpc_stats->ctl_array.ctl_pwr_table); + kfree(tpc_stats); + ar->debug.tpc_stats = NULL; + } +} + +static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, + struct sk_buff *skb) +{ + struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *fixed_param; + struct wmi_tpc_stats_arg *tpc_stats; + const struct wmi_tlv *tlv; + void *ptr = skb->data; + struct ath12k *ar; + u16 tlv_tag; + u32 event_count; + int ret; + + if (!skb->data) { + ath12k_warn(ab, "No data present in tpc stats event\n"); + return; + } + + if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) { + ath12k_warn(ab, "TPC stats event size invalid\n"); + return; + } + + tlv = (struct wmi_tlv *)ptr; + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + ptr += sizeof(*tlv); + + if (tlv_tag != WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM) { + ath12k_warn(ab, "TPC stats without fixed param tlv at start\n"); + return; + } + + fixed_param = (struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *)ptr; + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(fixed_param->pdev_id) + 1); + if (!ar) { + ath12k_warn(ab, "Failed to get ar for tpc stats\n"); + rcu_read_unlock(); + return; + } + spin_lock_bh(&ar->data_lock); + if (!ar->debug.tpc_request) { + /* Event is received either without request or the + * timeout, if memory is already allocated free it + */ + if (ar->debug.tpc_stats) { + ath12k_warn(ab, "Freeing memory for tpc_stats\n"); + ath12k_wmi_free_tpc_stats_mem(ar); + } + goto unlock; + } + + event_count = le32_to_cpu(fixed_param->event_count); + if (event_count == 0) { + if (ar->debug.tpc_stats) { + ath12k_warn(ab, + "Invalid tpc memory present\n"); + goto unlock; + } + ar->debug.tpc_stats = + kzalloc(sizeof(struct wmi_tpc_stats_arg), + GFP_ATOMIC); + if (!ar->debug.tpc_stats) { + ath12k_warn(ab, + "Failed to allocate memory for tpc stats\n"); + goto unlock; + } + } + + tpc_stats = ar->debug.tpc_stats; + if (!tpc_stats) { + ath12k_warn(ab, "tpc stats memory unavailable\n"); + goto unlock; + } + + if (!(event_count == 0)) { + if (event_count != tpc_stats->event_count + 1) { + ath12k_warn(ab, + "Invalid tpc event received\n"); + goto unlock; + } + } + tpc_stats->pdev_id = le32_to_cpu(fixed_param->pdev_id); + tpc_stats->end_of_event = le32_to_cpu(fixed_param->end_of_event); + tpc_stats->event_count = le32_to_cpu(fixed_param->event_count); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "tpc stats event_count %d\n", + tpc_stats->event_count); + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tpc_stats_event_parser, + tpc_stats); + if (ret) { + ath12k_wmi_free_tpc_stats_mem(ar); + ath12k_warn(ab, "failed to parse tpc_stats tlv: %d\n", ret); + goto unlock; + } + + if (tpc_stats->end_of_event) + complete(&ar->debug.tpc_complete); + +unlock: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} +#else +static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, + struct sk_buff *skb) +{ +} +#endif + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -6958,13 +9403,6 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_P2P_NOA_EVENTID: ath12k_wmi_p2p_noa_event(ab, skb); break; - /* add Unsupported events here */ - case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: - case WMI_PEER_OPER_MODE_CHANGE_EVENTID: - case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID: - ath12k_dbg(ab, ATH12K_DBG_WMI, - "ignoring unsupported event 0x%x\n", id); - break; case WMI_PDEV_DFS_RADAR_DETECTION_EVENTID: ath12k_wmi_pdev_dfs_radar_detected_event(ab, skb); break; @@ -6974,7 +9412,43 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_DIAG_EVENTID: ath12k_wmi_diag_event(ab, skb); break; - /* TODO: Add remaining events */ + case WMI_WOW_WAKEUP_HOST_EVENTID: + ath12k_wmi_event_wow_wakeup_host(ab, skb); + break; + case WMI_GTK_OFFLOAD_STATUS_EVENTID: + ath12k_wmi_gtk_offload_status_event(ab, skb); + break; + case WMI_MLO_SETUP_COMPLETE_EVENTID: + ath12k_wmi_event_mlo_setup_complete(ab, skb); + break; + case WMI_MLO_TEARDOWN_COMPLETE_EVENTID: + ath12k_wmi_event_teardown_complete(ab, skb); + break; + case WMI_HALPHY_STATS_CTRL_PATH_EVENTID: + ath12k_wmi_process_tpc_stats(ab, skb); + break; + case WMI_11D_NEW_COUNTRY_EVENTID: + ath12k_reg_11d_new_cc_event(ab, skb); + break; + /* add Unsupported events (rare) here */ + case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: + case WMI_PEER_OPER_MODE_CHANGE_EVENTID: + case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID: + ath12k_dbg(ab, ATH12K_DBG_WMI, + "ignoring unsupported event 0x%x\n", id); + break; + /* add Unsupported events (frequent) here */ + case WMI_PDEV_GET_HALPHY_CAL_STATUS_EVENTID: + case WMI_MGMT_RX_FW_CONSUMED_EVENTID: + case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: + /* debug might flood hence silently ignore (no-op) */ + break; + case WMI_PDEV_UTF_EVENTID: + if (test_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags)) + ath12k_tm_wmi_event_segmented(ab, id, skb); + else + ath12k_tm_wmi_event_unsegmented(ab, id, skb); + break; default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); break; @@ -6988,9 +9462,11 @@ static int ath12k_connect_pdev_htc_service(struct ath12k_base *ab, u32 pdev_idx) { int status; - u32 svc_id[] = { ATH12K_HTC_SVC_ID_WMI_CONTROL, - ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1, - ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2 }; + static const u32 svc_id[] = { + ATH12K_HTC_SVC_ID_WMI_CONTROL, + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2 + }; struct ath12k_htc_svc_conn_req conn_req = {}; struct ath12k_htc_svc_conn_resp conn_resp = {}; @@ -7076,13 +9552,13 @@ ath12k_wmi_send_unit_test_cmd(struct ath12k *ar, int ath12k_wmi_simulate_radar(struct ath12k *ar) { - struct ath12k_vif *arvif; + struct ath12k_link_vif *arvif; u32 dfs_args[DFS_MAX_TEST_ARGS]; struct wmi_unit_test_cmd wmi_ut; bool arvif_found = false; list_for_each_entry(arvif, &ar->arvifs, list) { - if (arvif->is_started && arvif->vdev_type == WMI_VDEV_TYPE_AP) { + if (arvif->is_started && arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP) { arvif_found = true; break; } @@ -7109,6 +9585,74 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); } +int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type) +{ + struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_tlv *tlv; + __le32 *pdev_id; + u32 buf_len; + void *ptr; + int ret; + + buf_len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(u32) + TLV_HDR_SIZE + TLV_HDR_SIZE; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + if (!skb) + return -ENOMEM; + cmd = (struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM, + sizeof(*cmd)); + + cmd->stats_id_mask = cpu_to_le32(WMI_REQ_CTRL_PATH_PDEV_TX_STAT); + cmd->action = cpu_to_le32(WMI_REQUEST_CTRL_PATH_STAT_GET); + cmd->subid = cpu_to_le32(tpc_stats_type); + + ptr = skb->data + sizeof(*cmd); + + /* The below TLV arrays optionally follow this fixed param TLV structure + * 1. ARRAY_UINT32 pdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the pdevs identified in the array. + * 2. ARRAY_UNIT32 vdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the vdevs identified in the array. + * 3. ath12k_wmi_mac_addr_params peer_macaddr[]; + * If this array is present and non-zero length, stats should only + * be provided from the peers with the MAC addresses specified + * in the array + */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); + ptr += TLV_HDR_SIZE; + + pdev_id = ptr; + *pdev_id = cpu_to_le32(ath12k_mac_get_target_pdev_id(ar)); + ptr += sizeof(*pdev_id); + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + ptr += TLV_HDR_SIZE; + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_FIXED_STRUCT, 0); + ptr += TLV_HDR_SIZE; + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID); + if (ret) { + ath12k_warn(ar->ab, + "failed to submit WMI_REQUEST_STATS_CTRL_PATH_CMDID\n"); + dev_kfree_skb(skb); + return ret; + } + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI get TPC STATS sent on pdev %d\n", + ar->pdev->pdev_id); + + return ret; +} + int ath12k_wmi_connect(struct ath12k_base *ab) { u32 i; @@ -7183,3 +9727,996 @@ void ath12k_wmi_detach(struct ath12k_base *ab) ath12k_wmi_free_dbring_caps(ab); } + +int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_arg *arg) +{ + struct wmi_hw_data_filter_cmd *cmd; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_hw_data_filter_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HW_DATA_FILTER_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arg->vdev_id); + cmd->enable = cpu_to_le32(arg->enable ? 1 : 0); + + /* Set all modes in case of disable */ + if (arg->enable) + cmd->hw_filter_bitmap = cpu_to_le32(arg->hw_filter_bitmap); + else + cmd->hw_filter_bitmap = cpu_to_le32((u32)~0U); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi hw data filter enable %d filter_bitmap 0x%x\n", + arg->enable, arg->hw_filter_bitmap); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); +} + +int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) +{ + struct wmi_wow_host_wakeup_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_host_wakeup_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + sizeof(*cmd)); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); +} + +int ath12k_wmi_wow_enable(struct ath12k *ar) +{ + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_enable_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ENABLE_CMD, + sizeof(*cmd)); + + cmd->enable = cpu_to_le32(1); + cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n"); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); +} + +int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, + enum wmi_wow_wakeup_event event, + u32 enable) +{ + struct wmi_wow_add_del_event_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_add_del_event_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ADD_DEL_EVT_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->is_add = cpu_to_le32(enable); + cmd->event_bitmap = cpu_to_le32((1 << event)); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n", + wow_wakeup_event(event), enable, vdev_id); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); +} + +int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, + const u8 *pattern, const u8 *mask, + int pattern_len, int pattern_offset) +{ + struct wmi_wow_add_pattern_cmd *cmd; + struct wmi_wow_bitmap_pattern_params *bitmap; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*cmd) + + sizeof(*tlv) + /* array struct */ + sizeof(*bitmap) + /* bitmap */ + sizeof(*tlv) + /* empty ipv4 sync */ + sizeof(*tlv) + /* empty ipv6 sync */ + sizeof(*tlv) + /* empty magic */ + sizeof(*tlv) + /* empty info timeout */ + sizeof(*tlv) + sizeof(u32); /* ratelimit interval */ + + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + /* cmd */ + ptr = skb->data; + cmd = ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ADD_PATTERN_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pattern_id = cpu_to_le32(pattern_id); + cmd->pattern_type = cpu_to_le32(WOW_BITMAP_PATTERN); + + ptr += sizeof(*cmd); + + /* bitmap */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, sizeof(*bitmap)); + + ptr += sizeof(*tlv); + + bitmap = ptr; + bitmap->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_BITMAP_PATTERN_T, + sizeof(*bitmap)); + memcpy(bitmap->patternbuf, pattern, pattern_len); + memcpy(bitmap->bitmaskbuf, mask, pattern_len); + bitmap->pattern_offset = cpu_to_le32(pattern_offset); + bitmap->pattern_len = cpu_to_le32(pattern_len); + bitmap->bitmask_len = cpu_to_le32(pattern_len); + bitmap->pattern_id = cpu_to_le32(pattern_id); + + ptr += sizeof(*bitmap); + + /* ipv4 sync */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0); + + ptr += sizeof(*tlv); + + /* ipv6 sync */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0); + + ptr += sizeof(*tlv); + + /* magic */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0); + + ptr += sizeof(*tlv); + + /* pattern info timeout */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + + ptr += sizeof(*tlv); + + /* ratelimit interval */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d pattern_len %d\n", + vdev_id, pattern_id, pattern_offset, pattern_len); + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow pattern: ", + bitmap->patternbuf, pattern_len); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow bitmask: ", + bitmap->bitmaskbuf, pattern_len); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); +} + +int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) +{ + struct wmi_wow_del_pattern_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_del_pattern_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_DEL_PATTERN_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pattern_id = cpu_to_le32(pattern_id); + cmd->pattern_type = cpu_to_le32(WOW_BITMAP_PATTERN); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n", + vdev_id, pattern_id); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); +} + +static struct sk_buff * +ath12k_wmi_op_gen_config_pno_start(struct ath12k *ar, u32 vdev_id, + struct wmi_pno_scan_req_arg *pno) +{ + struct nlo_configured_params *nlo_list; + size_t len, nlo_list_len, channel_list_len; + struct wmi_wow_nlo_config_cmd *cmd; + __le32 *channel_list; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + u32 i; + + len = sizeof(*cmd) + + sizeof(*tlv) + + /* TLV place holder for array of structures + * nlo_configured_params(nlo_list) + */ + sizeof(*tlv); + /* TLV place holder for array of uint32 channel_list */ + + channel_list_len = sizeof(u32) * pno->a_networks[0].channel_count; + len += channel_list_len; + + nlo_list_len = sizeof(*nlo_list) * pno->uc_networks_count; + len += nlo_list_len; + + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = skb->data; + cmd = ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_NLO_CONFIG_CMD, sizeof(*cmd)); + + cmd->vdev_id = cpu_to_le32(pno->vdev_id); + cmd->flags = cpu_to_le32(WMI_NLO_CONFIG_START | WMI_NLO_CONFIG_SSID_HIDE_EN); + + /* current FW does not support min-max range for dwell time */ + cmd->active_dwell_time = cpu_to_le32(pno->active_max_time); + cmd->passive_dwell_time = cpu_to_le32(pno->passive_max_time); + + if (pno->do_passive_scan) + cmd->flags |= cpu_to_le32(WMI_NLO_CONFIG_SCAN_PASSIVE); + + cmd->fast_scan_period = cpu_to_le32(pno->fast_scan_period); + cmd->slow_scan_period = cpu_to_le32(pno->slow_scan_period); + cmd->fast_scan_max_cycles = cpu_to_le32(pno->fast_scan_max_cycles); + cmd->delay_start_time = cpu_to_le32(pno->delay_start_time); + + if (pno->enable_pno_scan_randomization) { + cmd->flags |= cpu_to_le32(WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ | + WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ); + ether_addr_copy(cmd->mac_addr.addr, pno->mac_addr); + ether_addr_copy(cmd->mac_mask.addr, pno->mac_addr_mask); + } + + ptr += sizeof(*cmd); + + /* nlo_configured_params(nlo_list) */ + cmd->no_of_ssids = cpu_to_le32(pno->uc_networks_count); + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, nlo_list_len); + + ptr += sizeof(*tlv); + nlo_list = ptr; + for (i = 0; i < pno->uc_networks_count; i++) { + tlv = (struct wmi_tlv *)(&nlo_list[i].tlv_header); + tlv->header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_ARRAY_BYTE, + sizeof(*nlo_list)); + + nlo_list[i].ssid.valid = cpu_to_le32(1); + nlo_list[i].ssid.ssid.ssid_len = + cpu_to_le32(pno->a_networks[i].ssid.ssid_len); + memcpy(nlo_list[i].ssid.ssid.ssid, + pno->a_networks[i].ssid.ssid, + le32_to_cpu(nlo_list[i].ssid.ssid.ssid_len)); + + if (pno->a_networks[i].rssi_threshold && + pno->a_networks[i].rssi_threshold > -300) { + nlo_list[i].rssi_cond.valid = cpu_to_le32(1); + nlo_list[i].rssi_cond.rssi = + cpu_to_le32(pno->a_networks[i].rssi_threshold); + } + + nlo_list[i].bcast_nw_type.valid = cpu_to_le32(1); + nlo_list[i].bcast_nw_type.bcast_nw_type = + cpu_to_le32(pno->a_networks[i].bcast_nw_type); + } + + ptr += nlo_list_len; + cmd->num_of_channels = cpu_to_le32(pno->a_networks[0].channel_count); + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, channel_list_len); + ptr += sizeof(*tlv); + channel_list = ptr; + + for (i = 0; i < pno->a_networks[0].channel_count; i++) + channel_list[i] = cpu_to_le32(pno->a_networks[0].channels[i]); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv start pno config vdev_id %d\n", + vdev_id); + + return skb; +} + +static struct sk_buff *ath12k_wmi_op_gen_config_pno_stop(struct ath12k *ar, + u32 vdev_id) +{ + struct wmi_wow_nlo_config_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_wow_nlo_config_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_NLO_CONFIG_CMD, len); + + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->flags = cpu_to_le32(WMI_NLO_CONFIG_STOP); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi tlv stop pno config vdev_id %d\n", vdev_id); + return skb; +} + +int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, + struct wmi_pno_scan_req_arg *pno_scan) +{ + struct sk_buff *skb; + + if (pno_scan->enable) + skb = ath12k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan); + else + skb = ath12k_wmi_op_gen_config_pno_stop(ar, vdev_id); + + if (IS_ERR_OR_NULL(skb)) + return -ENOMEM; + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); +} + +static void ath12k_wmi_fill_ns_offload(struct ath12k *ar, + struct wmi_arp_ns_offload_arg *offload, + void **ptr, + bool enable, + bool ext) +{ + struct wmi_ns_offload_params *ns; + struct wmi_tlv *tlv; + void *buf_ptr = *ptr; + u32 ns_cnt, ns_ext_tuples; + int i, max_offloads; + + ns_cnt = offload->ipv6_count; + + tlv = buf_ptr; + + if (ext) { + ns_ext_tuples = offload->ipv6_count - WMI_MAX_NS_OFFLOADS; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + ns_ext_tuples * sizeof(*ns)); + i = WMI_MAX_NS_OFFLOADS; + max_offloads = offload->ipv6_count; + } else { + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + WMI_MAX_NS_OFFLOADS * sizeof(*ns)); + i = 0; + max_offloads = WMI_MAX_NS_OFFLOADS; + } + + buf_ptr += sizeof(*tlv); + + for (; i < max_offloads; i++) { + ns = buf_ptr; + ns->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_NS_OFFLOAD_TUPLE, + sizeof(*ns)); + + if (enable) { + if (i < ns_cnt) + ns->flags |= cpu_to_le32(WMI_NSOL_FLAGS_VALID); + + memcpy(ns->target_ipaddr[0], offload->ipv6_addr[i], 16); + memcpy(ns->solicitation_ipaddr, offload->self_ipv6_addr[i], 16); + + if (offload->ipv6_type[i]) + ns->flags |= cpu_to_le32(WMI_NSOL_FLAGS_IS_IPV6_ANYCAST); + + memcpy(ns->target_mac.addr, offload->mac_addr, ETH_ALEN); + + if (!is_zero_ether_addr(ns->target_mac.addr)) + ns->flags |= cpu_to_le32(WMI_NSOL_FLAGS_MAC_VALID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi index %d ns_solicited %pI6 target %pI6", + i, ns->solicitation_ipaddr, + ns->target_ipaddr[0]); + } + + buf_ptr += sizeof(*ns); + } + + *ptr = buf_ptr; +} + +static void ath12k_wmi_fill_arp_offload(struct ath12k *ar, + struct wmi_arp_ns_offload_arg *offload, + void **ptr, + bool enable) +{ + struct wmi_arp_offload_params *arp; + struct wmi_tlv *tlv; + void *buf_ptr = *ptr; + int i; + + /* fill arp tuple */ + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + WMI_MAX_ARP_OFFLOADS * sizeof(*arp)); + buf_ptr += sizeof(*tlv); + + for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) { + arp = buf_ptr; + arp->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_ARP_OFFLOAD_TUPLE, + sizeof(*arp)); + + if (enable && i < offload->ipv4_count) { + /* Copy the target ip addr and flags */ + arp->flags = cpu_to_le32(WMI_ARPOL_FLAGS_VALID); + memcpy(arp->target_ipaddr, offload->ipv4_addr[i], 4); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi arp offload address %pI4", + arp->target_ipaddr); + } + + buf_ptr += sizeof(*arp); + } + + *ptr = buf_ptr; +} + +int ath12k_wmi_arp_ns_offload(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct wmi_arp_ns_offload_arg *offload, + bool enable) +{ + struct wmi_set_arp_ns_offload_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *buf_ptr; + size_t len; + u8 ns_cnt, ns_ext_tuples = 0; + + ns_cnt = offload->ipv6_count; + + len = sizeof(*cmd) + + sizeof(*tlv) + + WMI_MAX_NS_OFFLOADS * sizeof(struct wmi_ns_offload_params) + + sizeof(*tlv) + + WMI_MAX_ARP_OFFLOADS * sizeof(struct wmi_arp_offload_params); + + if (ns_cnt > WMI_MAX_NS_OFFLOADS) { + ns_ext_tuples = ns_cnt - WMI_MAX_NS_OFFLOADS; + len += sizeof(*tlv) + + ns_ext_tuples * sizeof(struct wmi_ns_offload_params); + } + + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + buf_ptr = skb->data; + cmd = buf_ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_SET_ARP_NS_OFFLOAD_CMD, + sizeof(*cmd)); + cmd->flags = cpu_to_le32(0); + cmd->vdev_id = cpu_to_le32(arvif->vdev_id); + cmd->num_ns_ext_tuples = cpu_to_le32(ns_ext_tuples); + + buf_ptr += sizeof(*cmd); + + ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 0); + ath12k_wmi_fill_arp_offload(ar, offload, &buf_ptr, enable); + + if (ns_ext_tuples) + ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); +} + +int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, + struct ath12k_link_vif *arvif, bool enable) +{ + struct ath12k_rekey_data *rekey_data = &arvif->rekey_data; + struct wmi_gtk_rekey_offload_cmd *cmd; + struct sk_buff *skb; + __le64 replay_ctr; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_GTK_OFFLOAD_CMD, sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arvif->vdev_id); + + if (enable) { + cmd->flags = cpu_to_le32(GTK_OFFLOAD_ENABLE_OPCODE); + + /* the length in rekey_data and cmd is equal */ + memcpy(cmd->kck, rekey_data->kck, sizeof(cmd->kck)); + memcpy(cmd->kek, rekey_data->kek, sizeof(cmd->kek)); + + replay_ctr = cpu_to_le64(rekey_data->replay_ctr); + memcpy(cmd->replay_ctr, &replay_ctr, + sizeof(replay_ctr)); + } else { + cmd->flags = cpu_to_le32(GTK_OFFLOAD_DISABLE_OPCODE); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n", + arvif->vdev_id, enable); + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); +} + +int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, + struct ath12k_link_vif *arvif) +{ + struct wmi_gtk_rekey_offload_cmd *cmd; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_GTK_OFFLOAD_CMD, sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arvif->vdev_id); + cmd->flags = cpu_to_le32(GTK_OFFLOAD_REQUEST_STATUS_OPCODE); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n", + arvif->vdev_id); + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); +} + +int ath12k_wmi_sta_keepalive(struct ath12k *ar, + const struct wmi_sta_keepalive_arg *arg) +{ + struct wmi_sta_keepalive_arp_resp_params *arp; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_sta_keepalive_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd) + sizeof(*arp); + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_keepalive_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_STA_KEEPALIVE_CMD, sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arg->vdev_id); + cmd->enabled = cpu_to_le32(arg->enabled); + cmd->interval = cpu_to_le32(arg->interval); + cmd->method = cpu_to_le32(arg->method); + + arp = (struct wmi_sta_keepalive_arp_resp_params *)(cmd + 1); + arp->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_STA_KEEPALVE_ARP_RESPONSE, + sizeof(*arp)); + if (arg->method == WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE || + arg->method == WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST) { + arp->src_ip4_addr = cpu_to_le32(arg->src_ip4_addr); + arp->dest_ip4_addr = cpu_to_le32(arg->dest_ip4_addr); + ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi sta keepalive vdev %d enabled %d method %d interval %d\n", + arg->vdev_id, arg->enabled, arg->method, arg->interval); + + return ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); +} + +int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params) +{ + struct wmi_mlo_setup_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + u32 *partner_links, num_links; + int i, ret, buf_len, arg_len; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + + num_links = mlo_params->num_partner_links; + arg_len = num_links * sizeof(u32); + buf_len = sizeof(*cmd) + TLV_HDR_SIZE + arg_len; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mlo_setup_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_SETUP_CMD, + sizeof(*cmd)); + cmd->mld_group_id = mlo_params->group_id; + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + ptr = skb->data + sizeof(*cmd); + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, arg_len); + ptr += TLV_HDR_SIZE; + + partner_links = ptr; + for (i = 0; i < num_links; i++) + partner_links[i] = mlo_params->partner_link_id[i]; + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_MLO_SETUP_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to submit WMI_MLO_SETUP_CMDID command: %d\n", + ret); + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + +int ath12k_wmi_mlo_ready(struct ath12k *ar) +{ + struct wmi_mlo_ready_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mlo_ready_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_READY_CMD, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_MLO_READY_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to submit WMI_MLO_READY_CMDID command: %d\n", + ret); + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + +int ath12k_wmi_mlo_teardown(struct ath12k *ar) +{ + struct wmi_mlo_teardown_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mlo_teardown_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_TEARDOWN_CMD, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + cmd->reason_code = WMI_MLO_TEARDOWN_SSR_REASON; + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_MLO_TEARDOWN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to submit WMI MLO teardown command: %d\n", + ret); + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + +bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar) +{ + return test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, + ar->ab->wmi_ab.svc_map) && ar->supports_6ghz; +} + +int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, + u32 vdev_id, + struct ath12k_reg_tpc_power_info *param) +{ + struct wmi_vdev_set_tpc_power_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_vdev_ch_power_params *ch; + int i, ret, len, array_len; + struct sk_buff *skb; + struct wmi_tlv *tlv; + u8 *ptr; + + array_len = sizeof(*ch) * param->num_pwr_levels; + len = sizeof(*cmd) + TLV_HDR_SIZE + array_len; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_SET_TPC_POWER_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->psd_power = cpu_to_le32(param->is_psd_power); + cmd->eirp_power = cpu_to_le32(param->eirp_power); + cmd->power_type_6ghz = cpu_to_le32(param->ap_power_type); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "tpc vdev id %d is psd power %d eirp power %d 6 ghz power type %d\n", + vdev_id, param->is_psd_power, param->eirp_power, param->ap_power_type); + + ptr += sizeof(*cmd); + tlv = (struct wmi_tlv *)ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, array_len); + + ptr += TLV_HDR_SIZE; + ch = (struct wmi_vdev_ch_power_params *)ptr; + + for (i = 0; i < param->num_pwr_levels; i++, ch++) { + ch->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_CH_POWER_INFO, + sizeof(*ch)); + ch->chan_cfreq = cpu_to_le32(param->chan_power_info[i].chan_cfreq); + ch->tx_power = cpu_to_le32(param->chan_power_info[i].tx_power); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc chan freq %d TX power %d\n", + ch->chan_cfreq, ch->tx_power); + } + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_TPC_POWER_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n"); + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + +static int +ath12k_wmi_fill_disallowed_bmap(struct ath12k_base *ab, + struct wmi_disallowed_mlo_mode_bitmap_params *dislw_bmap, + struct wmi_mlo_link_set_active_arg *arg) +{ + struct wmi_ml_disallow_mode_bmap_arg *dislw_bmap_arg; + u8 i; + + if (arg->num_disallow_mode_comb > + ARRAY_SIZE(arg->disallow_bmap)) { + ath12k_warn(ab, "invalid num_disallow_mode_comb: %d", + arg->num_disallow_mode_comb); + return -EINVAL; + } + + dislw_bmap_arg = &arg->disallow_bmap[0]; + for (i = 0; i < arg->num_disallow_mode_comb; i++) { + dislw_bmap->tlv_header = + ath12k_wmi_tlv_cmd_hdr(0, sizeof(*dislw_bmap)); + dislw_bmap->disallowed_mode_bitmap = + cpu_to_le32(dislw_bmap_arg->disallowed_mode); + dislw_bmap->ieee_link_id_comb = + le32_encode_bits(dislw_bmap_arg->ieee_link_id[0], + WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_1) | + le32_encode_bits(dislw_bmap_arg->ieee_link_id[1], + WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_2) | + le32_encode_bits(dislw_bmap_arg->ieee_link_id[2], + WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_3) | + le32_encode_bits(dislw_bmap_arg->ieee_link_id[3], + WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_4); + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "entry %d disallowed_mode %d ieee_link_id_comb 0x%x", + i, dislw_bmap_arg->disallowed_mode, + dislw_bmap_arg->ieee_link_id_comb); + dislw_bmap++; + dislw_bmap_arg++; + } + + return 0; +} + +int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, + struct wmi_mlo_link_set_active_arg *arg) +{ + struct wmi_disallowed_mlo_mode_bitmap_params *disallowed_mode_bmap; + struct wmi_mlo_set_active_link_number_params *link_num_param; + u32 num_link_num_param = 0, num_vdev_bitmap = 0; + struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab; + struct wmi_mlo_link_set_active_cmd *cmd; + u32 num_inactive_vdev_bitmap = 0; + u32 num_disallow_mode_comb = 0; + struct wmi_tlv *tlv; + struct sk_buff *skb; + __le32 *vdev_bitmap; + void *buf_ptr; + int i, ret; + u32 len; + + if (!arg->num_vdev_bitmap && !arg->num_link_entry) { + ath12k_warn(ab, "Invalid num_vdev_bitmap and num_link_entry"); + return -EINVAL; + } + + switch (arg->force_mode) { + case WMI_MLO_LINK_FORCE_MODE_ACTIVE_LINK_NUM: + case WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM: + num_link_num_param = arg->num_link_entry; + fallthrough; + case WMI_MLO_LINK_FORCE_MODE_ACTIVE: + case WMI_MLO_LINK_FORCE_MODE_INACTIVE: + case WMI_MLO_LINK_FORCE_MODE_NO_FORCE: + num_vdev_bitmap = arg->num_vdev_bitmap; + break; + case WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE: + num_vdev_bitmap = arg->num_vdev_bitmap; + num_inactive_vdev_bitmap = arg->num_inactive_vdev_bitmap; + break; + default: + ath12k_warn(ab, "Invalid force mode: %u", arg->force_mode); + return -EINVAL; + } + + num_disallow_mode_comb = arg->num_disallow_mode_comb; + len = sizeof(*cmd) + + TLV_HDR_SIZE + sizeof(*link_num_param) * num_link_num_param + + TLV_HDR_SIZE + sizeof(*vdev_bitmap) * num_vdev_bitmap + + TLV_HDR_SIZE + TLV_HDR_SIZE + TLV_HDR_SIZE + + TLV_HDR_SIZE + sizeof(*disallowed_mode_bmap) * num_disallow_mode_comb; + if (arg->force_mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE) + len += sizeof(*vdev_bitmap) * num_inactive_vdev_bitmap; + + skb = ath12k_wmi_alloc_skb(wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mlo_link_set_active_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_LINK_SET_ACTIVE_CMD, + sizeof(*cmd)); + cmd->force_mode = cpu_to_le32(arg->force_mode); + cmd->reason = cpu_to_le32(arg->reason); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "mode %d reason %d num_link_num_param %d num_vdev_bitmap %d inactive %d num_disallow_mode_comb %d", + arg->force_mode, arg->reason, num_link_num_param, + num_vdev_bitmap, num_inactive_vdev_bitmap, + num_disallow_mode_comb); + + buf_ptr = skb->data + sizeof(*cmd); + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + sizeof(*link_num_param) * num_link_num_param); + buf_ptr += TLV_HDR_SIZE; + + if (num_link_num_param) { + cmd->ctrl_flags = + le32_encode_bits(arg->ctrl_flags.dync_force_link_num ? 1 : 0, + CRTL_F_DYNC_FORCE_LINK_NUM); + + link_num_param = buf_ptr; + for (i = 0; i < num_link_num_param; i++) { + link_num_param->tlv_header = + ath12k_wmi_tlv_cmd_hdr(0, sizeof(*link_num_param)); + link_num_param->num_of_link = + cpu_to_le32(arg->link_num[i].num_of_link); + link_num_param->vdev_type = + cpu_to_le32(arg->link_num[i].vdev_type); + link_num_param->vdev_subtype = + cpu_to_le32(arg->link_num[i].vdev_subtype); + link_num_param->home_freq = + cpu_to_le32(arg->link_num[i].home_freq); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "entry %d num_of_link %d vdev type %d subtype %d freq %d control_flags %d", + i, arg->link_num[i].num_of_link, + arg->link_num[i].vdev_type, + arg->link_num[i].vdev_subtype, + arg->link_num[i].home_freq, + __le32_to_cpu(cmd->ctrl_flags)); + link_num_param++; + } + + buf_ptr += sizeof(*link_num_param) * num_link_num_param; + } + + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, + sizeof(*vdev_bitmap) * num_vdev_bitmap); + buf_ptr += TLV_HDR_SIZE; + + if (num_vdev_bitmap) { + vdev_bitmap = buf_ptr; + for (i = 0; i < num_vdev_bitmap; i++) { + vdev_bitmap[i] = cpu_to_le32(arg->vdev_bitmap[i]); + ath12k_dbg(ab, ATH12K_DBG_WMI, "entry %d vdev_id_bitmap 0x%x", + i, arg->vdev_bitmap[i]); + } + + buf_ptr += sizeof(*vdev_bitmap) * num_vdev_bitmap; + } + + if (arg->force_mode == WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE) { + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, + sizeof(*vdev_bitmap) * + num_inactive_vdev_bitmap); + buf_ptr += TLV_HDR_SIZE; + + if (num_inactive_vdev_bitmap) { + vdev_bitmap = buf_ptr; + for (i = 0; i < num_inactive_vdev_bitmap; i++) { + vdev_bitmap[i] = + cpu_to_le32(arg->inactive_vdev_bitmap[i]); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "entry %d inactive_vdev_id_bitmap 0x%x", + i, arg->inactive_vdev_bitmap[i]); + } + + buf_ptr += sizeof(*vdev_bitmap) * num_inactive_vdev_bitmap; + } + } else { + /* add empty vdev bitmap2 tlv */ + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + buf_ptr += TLV_HDR_SIZE; + } + + /* add empty ieee_link_id_bitmap tlv */ + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + buf_ptr += TLV_HDR_SIZE; + + /* add empty ieee_link_id_bitmap2 tlv */ + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + buf_ptr += TLV_HDR_SIZE; + + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + sizeof(*disallowed_mode_bmap) * + arg->num_disallow_mode_comb); + buf_ptr += TLV_HDR_SIZE; + + ret = ath12k_wmi_fill_disallowed_bmap(ab, buf_ptr, arg); + if (ret) + goto free_skb; + + ret = ath12k_wmi_cmd_send(&wmi_ab->wmi[0], skb, WMI_MLO_LINK_SET_ACTIVE_CMDID); + if (ret) { + ath12k_warn(ab, + "failed to send WMI_MLO_LINK_SET_ACTIVE_CMDID: %d\n", ret); + goto free_skb; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, "WMI mlo link set active cmd"); + + return ret; + +free_skb: + dev_kfree_skb(skb); + return ret; +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 103462feb935..c640ffa180c8 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_WMI_H @@ -24,6 +24,9 @@ struct ath12k_base; struct ath12k; +struct ath12k_link_vif; +struct ath12k_fw_stats; +struct ath12k_reg_tpc_power_info; /* There is no signed version of __le32, so for a temporary solution come * up with our own version. The idea is from fs/ntfs/endian.h. @@ -164,10 +167,6 @@ struct wmi_tlv { #define WLAN_SCAN_MAX_HINT_BSSID 10 #define MAX_RNR_BSS 5 -#define WLAN_SCAN_MAX_HINT_S_SSID 10 -#define WLAN_SCAN_MAX_HINT_BSSID 10 -#define MAX_RNR_BSS 5 - #define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1 #define WMI_BA_MODE_BUFFER_SIZE_256 3 @@ -218,9 +217,9 @@ enum wmi_host_hw_mode_priority { }; enum WMI_HOST_WLAN_BAND { - WMI_HOST_WLAN_2G_CAP = 1, - WMI_HOST_WLAN_5G_CAP = 2, - WMI_HOST_WLAN_2G_5G_CAP = 3, + WMI_HOST_WLAN_2GHZ_CAP = 1, + WMI_HOST_WLAN_5GHZ_CAP = 2, + WMI_HOST_WLAN_2GHZ_5GHZ_CAP = 3, }; enum wmi_cmd_group { @@ -288,6 +287,7 @@ enum wmi_cmd_group { WMI_GRP_TWT = 0x3e, WMI_GRP_MOTION_DET = 0x3f, WMI_GRP_SPATIAL_REUSE = 0x40, + WMI_GRP_MLO = 0x48, }; #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) @@ -357,6 +357,9 @@ enum wmi_tlv_cmd_id { WMI_PDEV_DMA_RING_CFG_REQ_CMDID, WMI_PDEV_HE_TB_ACTION_FRM_CMDID, WMI_PDEV_PKTLOG_FILTER_CMDID, + WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID = 0x4044, + WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID = 0x4045, + WMI_PDEV_SET_BIOS_INTERFACE_CMDID = 0x404A, WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV), WMI_VDEV_DELETE_CMDID, WMI_VDEV_START_REQUEST_CMDID, @@ -384,6 +387,22 @@ enum wmi_tlv_cmd_id { WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + WMI_VDEV_SET_ARP_STAT_CMDID, + WMI_VDEV_GET_ARP_STAT_CMDID, + WMI_VDEV_GET_TX_POWER_CMDID, + WMI_VDEV_LIMIT_OFFCHAN_CMDID, + WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID, + WMI_VDEV_CHAINMASK_CONFIG_CMDID, + WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID, + WMI_VDEV_GET_MWS_COEX_INFO_CMDID, + WMI_VDEV_DELETE_ALL_PEER_CMDID, + WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID, + WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID, + WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID, + WMI_VDEV_SET_PCL_CMDID, + WMI_VDEV_GET_BIG_DATA_CMDID, + WMI_VDEV_GET_BIG_DATA_P2_CMDID, + WMI_VDEV_SET_TPC_POWER_CMDID, WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), WMI_PEER_DELETE_CMDID, WMI_PEER_FLUSH_TIDS_CMDID, @@ -515,6 +534,9 @@ enum wmi_tlv_cmd_id { WMI_REQUEST_RCPI_CMDID, WMI_REQUEST_PEER_STATS_INFO_CMDID, WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_REQUEST_WLM_STATS_CMDID, + WMI_REQUEST_CTRL_PATH_STATS_CMDID, + WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID = WMI_REQUEST_CTRL_PATH_STATS_CMDID + 3, WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, @@ -665,6 +687,10 @@ enum wmi_tlv_cmd_id { WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID = WMI_TLV_CMD(WMI_GRP_SPATIAL_REUSE), WMI_PDEV_OBSS_PD_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMDID, + WMI_MLO_LINK_SET_ACTIVE_CMDID = WMI_TLV_CMD(WMI_GRP_MLO), + WMI_MLO_SETUP_CMDID, + WMI_MLO_READY_CMDID, + WMI_MLO_TEARDOWN_CMDID, }; enum wmi_tlv_event_id { @@ -706,6 +732,8 @@ enum wmi_tlv_event_id { WMI_PDEV_RAP_INFO_EVENTID, WMI_CHAN_RF_CHARACTERIZATION_INFO_EVENTID, WMI_SERVICE_READY_EXT2_EVENTID, + WMI_PDEV_GET_HALPHY_CAL_STATUS_EVENTID = + WMI_SERVICE_READY_EXT2_EVENTID + 4, WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV), WMI_VDEV_STOPPED_EVENTID, WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID, @@ -747,6 +775,7 @@ enum wmi_tlv_event_id { WMI_TBTTOFFSET_EXT_UPDATE_EVENTID, WMI_OFFCHAN_DATA_TX_COMPLETION_EVENTID, WMI_HOST_FILS_DISCOVERY_EVENTID, + WMI_MGMT_RX_FW_CONSUMED_EVENTID = WMI_HOST_FILS_DISCOVERY_EVENTID + 3, WMI_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_CMD(WMI_GRP_BA_NEG), WMI_TX_ADDBA_COMPLETE_EVENTID, WMI_BA_RSP_SSN_EVENTID, @@ -777,6 +806,9 @@ enum wmi_tlv_event_id { WMI_UPDATE_RCPI_EVENTID, WMI_PEER_STATS_INFO_EVENTID, WMI_RADIO_CHAN_STATS_EVENTID, + WMI_WLM_STATS_EVENTID, + WMI_CTRL_PATH_STATS_EVENTID, + WMI_HALPHY_STATS_CTRL_PATH_EVENTID, WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), WMI_NLO_SCAN_COMPLETE_EVENTID, WMI_APFIND_EVENTID, @@ -845,6 +877,8 @@ enum wmi_tlv_event_id { WMI_MDNS_STATS_EVENTID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), WMI_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), WMI_SAP_OFL_DEL_STA_EVENTID, + WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID = + WMI_EVT_GRP_START_ID(WMI_GRP_OBSS_OFL), WMI_OCB_SET_CONFIG_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_OCB), WMI_OCB_GET_TSF_TIMER_RESP_EVENTID, WMI_DCC_GET_STATS_RESP_EVENTID, @@ -874,6 +908,9 @@ enum wmi_tlv_event_id { WMI_TWT_DEL_DIALOG_EVENTID, WMI_TWT_PAUSE_DIALOG_EVENTID, WMI_TWT_RESUME_DIALOG_EVENTID, + WMI_MLO_LINK_SET_ACTIVE_RESP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MLO), + WMI_MLO_SETUP_COMPLETE_EVENTID, + WMI_MLO_TEARDOWN_COMPLETE_EVENTID, }; enum wmi_tlv_pdev_param { @@ -1178,6 +1215,7 @@ enum wmi_tlv_tag { WMI_TAG_ARRAY_BYTE, WMI_TAG_ARRAY_STRUCT, WMI_TAG_ARRAY_FIXED_STRUCT, + WMI_TAG_ARRAY_INT16, WMI_TAG_LAST_ARRAY_ENUM = 31, WMI_TAG_SERVICE_READY_EVENT, WMI_TAG_HAL_REG_CAPABILITIES, @@ -1928,7 +1966,34 @@ enum wmi_tlv_tag { WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F, WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, + WMI_TAG_TPC_STATS_GET_CMD = 0x38B, + WMI_TAG_TPC_STATS_EVENT_FIXED_PARAM, + WMI_TAG_TPC_STATS_CONFIG_EVENT, + WMI_TAG_TPC_STATS_REG_PWR_ALLOWED, + WMI_TAG_TPC_STATS_RATES_ARRAY, + WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT, + WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5, + WMI_TAG_VDEV_CH_POWER_INFO, + WMI_TAG_MLO_LINK_SET_ACTIVE_CMD = 0x3BE, WMI_TAG_EHT_RATE_SET = 0x3C4, + WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, + WMI_TAG_MLO_TX_SEND_PARAMS, + WMI_TAG_MLO_PARTNER_LINK_PARAMS, + WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC, + WMI_TAG_MLO_SETUP_CMD = 0x3C9, + WMI_TAG_MLO_SETUP_COMPLETE_EVENT, + WMI_TAG_MLO_READY_CMD, + WMI_TAG_MLO_TEARDOWN_CMD, + WMI_TAG_MLO_TEARDOWN_COMPLETE, + WMI_TAG_MLO_PEER_ASSOC_PARAMS = 0x3D0, + WMI_TAG_MLO_PEER_CREATE_PARAMS = 0x3D5, + WMI_TAG_MLO_VDEV_START_PARAMS = 0x3D6, + WMI_TAG_MLO_VDEV_CREATE_PARAMS = 0x3D7, + WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, + WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD = 0x3D9, + WMI_TAG_PDEV_SET_BIOS_INTERFACE_CMD = 0x3FB, + WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM = 0x442, + WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM, WMI_TAG_MAX }; @@ -2152,9 +2217,12 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213, WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219, WMI_TLV_SERVICE_EXT2_MSG = 220, + WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253, WMI_MAX_EXT_SERVICE = 256, + WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280, + WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, WMI_TLV_SERVICE_11BE = 289, @@ -2199,8 +2267,11 @@ enum wmi_peer_param { WMI_PEER_SET_MAX_TX_RATE = 17, WMI_PEER_SET_MIN_TX_RATE = 18, WMI_PEER_SET_DEFAULT_ROUTING = 19, + WMI_PEER_CHWIDTH_PUNCTURE_20MHZ_BITMAP = 39, }; +#define WMI_PEER_PUNCTURE_BITMAP GENMASK(23, 8) + enum wmi_slot_time { WMI_VDEV_SLOT_TIME_LONG = 1, WMI_VDEV_SLOT_TIME_SHORT = 2, @@ -2287,6 +2358,13 @@ struct ath12k_wmi_host_mem_chunk_arg { u32 req_id; }; +enum ath12k_peer_metadata_version { + ATH12K_PEER_METADATA_V0, + ATH12K_PEER_METADATA_V1, + ATH12K_PEER_METADATA_V1A, + ATH12K_PEER_METADATA_V1B +}; + struct ath12k_wmi_resource_config_arg { u32 num_vdevs; u32 num_peers; @@ -2349,8 +2427,10 @@ struct ath12k_wmi_resource_config_arg { u32 sched_params; u32 twt_ap_pdev_count; u32 twt_ap_sta_count; + enum ath12k_peer_metadata_version peer_metadata_ver; + u32 ema_max_vap_cnt; + u32 ema_max_profile_period; bool is_reg_cc_ext_event_supported; - u8 dp_peer_meta_data_ver; }; struct ath12k_wmi_init_cmd_arg { @@ -2403,7 +2483,10 @@ struct wmi_init_cmd { } __packed; #define WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT 4 +#define WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT 12 #define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4) +#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) +#define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) struct ath12k_wmi_resource_config_params { __le32 tlv_header; @@ -2535,6 +2618,8 @@ struct ath12k_wmi_soc_mac_phy_hw_mode_caps_params { __le32 num_chainmask_tables; } __packed; +#define WMI_HW_MODE_CAP_CFG_TYPE GENMASK(27, 0) + struct ath12k_wmi_hw_mode_cap_params { __le32 tlv_header; __le32 hw_mode_id; @@ -2584,6 +2669,12 @@ struct ath12k_wmi_mac_phy_caps_params { __le32 he_cap_info_2g_ext; __le32 he_cap_info_5g_ext; __le32 he_cap_info_internal; + __le32 wireless_modes; + __le32 low_2ghz_chan_freq; + __le32 high_2ghz_chan_freq; + __le32 low_5ghz_chan_freq; + __le32 high_5ghz_chan_freq; + __le32 nss_ratio; } __packed; struct ath12k_wmi_hal_reg_caps_ext_params { @@ -2604,6 +2695,19 @@ struct ath12k_wmi_soc_hal_reg_caps_params { __le32 num_phy; } __packed; +enum wmi_channel_width { + WMI_CHAN_WIDTH_20 = 0, + WMI_CHAN_WIDTH_40 = 1, + WMI_CHAN_WIDTH_80 = 2, + WMI_CHAN_WIDTH_160 = 3, + WMI_CHAN_WIDTH_80P80 = 4, + WMI_CHAN_WIDTH_5 = 5, + WMI_CHAN_WIDTH_10 = 6, + WMI_CHAN_WIDTH_165 = 7, + WMI_CHAN_WIDTH_160P160 = 8, + WMI_CHAN_WIDTH_320 = 9, +}; + #define WMI_MAX_EHTCAP_MAC_SIZE 2 #define WMI_MAX_EHTCAP_PHY_SIZE 3 #define WMI_MAX_EHTCAP_RATE_SET 3 @@ -2617,8 +2721,8 @@ struct ath12k_wmi_soc_hal_reg_caps_params { * 2 - index for 160 MHz, first 3 bytes valid * 3 - index for 320 MHz, first 3 bytes valid */ -#define WMI_MAX_EHT_SUPP_MCS_2G_SIZE 2 -#define WMI_MAX_EHT_SUPP_MCS_5G_SIZE 4 +#define WMI_MAX_EHT_SUPP_MCS_2GHZ_SIZE 2 +#define WMI_MAX_EHT_SUPP_MCS_5GHZ_SIZE 4 #define WMI_EHTCAP_TXRX_MCS_NSS_IDX_80 0 #define WMI_EHTCAP_TXRX_MCS_NSS_IDX_160 1 @@ -2644,6 +2748,11 @@ struct wmi_service_ready_ext2_event { __le32 default_num_msduq_supported_per_tid; } __packed; +struct ath12k_wmi_dbs_or_sbs_cap_params { + __le32 hw_mode_id; + __le32 sbs_lower_band_end_freq; +} __packed; + struct ath12k_wmi_caps_ext_params { __le32 hw_mode_id; __le32 pdev_and_hw_link_ids; @@ -2657,8 +2766,10 @@ struct ath12k_wmi_caps_ext_params { struct ath12k_wmi_ppe_threshold_params eht_ppet_2ghz; struct ath12k_wmi_ppe_threshold_params eht_ppet_5ghz; __le32 eht_cap_info_internal; - __le32 eht_supp_mcs_ext_2ghz[WMI_MAX_EHT_SUPP_MCS_2G_SIZE]; - __le32 eht_supp_mcs_ext_5ghz[WMI_MAX_EHT_SUPP_MCS_5G_SIZE]; + __le32 eht_supp_mcs_ext_2ghz[WMI_MAX_EHT_SUPP_MCS_2GHZ_SIZE]; + __le32 eht_supp_mcs_ext_5ghz[WMI_MAX_EHT_SUPP_MCS_5GHZ_SIZE]; + __le32 eml_capability; + __le32 mld_capability; } __packed; /* 2 word representation of MAC addr */ @@ -2707,6 +2818,9 @@ struct ath12k_wmi_vdev_create_arg { } chains[NUM_NL80211_BANDS]; u32 pdev_id; u8 if_stats_id; + u32 mbssid_flags; + u32 mbssid_tx_vdev_id; + u8 mld_addr[ETH_ALEN]; }; #define ATH12K_MAX_VDEV_STATS_ID 0x30 @@ -2728,9 +2842,36 @@ struct wmi_vdev_create_cmd { struct ath12k_wmi_vdev_txrx_streams_params { __le32 tlv_header; - u32 band; - u32 supported_tx_streams; - u32 supported_rx_streams; + __le32 band; + __le32 supported_tx_streams; + __le32 supported_rx_streams; +} __packed; + +struct wmi_vdev_create_mlo_params { + __le32 tlv_header; + struct ath12k_wmi_mac_addr_params mld_macaddr; +} __packed; + +#define ATH12K_WMI_FLAG_MLO_ENABLED BIT(0) +#define ATH12K_WMI_FLAG_MLO_ASSOC_LINK BIT(1) +#define ATH12K_WMI_FLAG_MLO_PRIMARY_UMAC BIT(2) +#define ATH12K_WMI_FLAG_MLO_LOGICAL_LINK_IDX_VALID BIT(3) +#define ATH12K_WMI_FLAG_MLO_PEER_ID_VALID BIT(4) +#define ATH12K_WMI_FLAG_MLO_MCAST_VDEV BIT(5) +#define ATH12K_WMI_FLAG_MLO_EMLSR_SUPPORT BIT(6) +#define ATH12K_WMI_FLAG_MLO_FORCED_INACTIVE BIT(7) +#define ATH12K_WMI_FLAG_MLO_LINK_ADD BIT(8) + +struct wmi_vdev_start_mlo_params { + __le32 tlv_header; + __le32 flags; +} __packed; + +struct wmi_partner_link_info { + __le32 tlv_header; + __le32 vdev_id; + __le32 hw_link_id; + struct ath12k_wmi_mac_addr_params vdev_addr; } __packed; struct wmi_vdev_delete_cmd { @@ -2738,14 +2879,23 @@ struct wmi_vdev_delete_cmd { __le32 vdev_id; } __packed; +struct ath12k_wmi_vdev_up_params { + u32 vdev_id; + u32 aid; + const u8 *bssid; + const u8 *tx_bssid; + u32 nontx_profile_idx; + u32 nontx_profile_cnt; +}; + struct wmi_vdev_up_cmd { __le32 tlv_header; __le32 vdev_id; __le32 vdev_assoc_id; struct ath12k_wmi_mac_addr_params vdev_bssid; - struct ath12k_wmi_mac_addr_params trans_bssid; - __le32 profile_idx; - __le32 profile_num; + struct ath12k_wmi_mac_addr_params tx_vdev_bssid; + __le32 nontx_profile_idx; + __le32 nontx_profile_cnt; } __packed; struct wmi_vdev_stop_cmd { @@ -2773,6 +2923,10 @@ struct ath12k_wmi_ssid_params { enum wmi_vdev_mbssid_flags { WMI_VDEV_MBSSID_FLAGS_NON_MBSSID_AP = BIT(0), + WMI_VDEV_MBSSID_FLAGS_TRANSMIT_AP = BIT(1), + WMI_VDEV_MBSSID_FLAGS_NON_TRANSMIT_AP = BIT(2), + WMI_VDEV_MBSSID_FLAGS_EMA_MODE = BIT(3), + WMI_VDEV_MBSSID_FLAGS_SCAN_MODE_VAP = BIT(4), }; struct wmi_vdev_start_request_cmd { @@ -2863,6 +3017,27 @@ enum wmi_phy_mode { MODE_MAX = 33, }; +#define ATH12K_WMI_MLO_MAX_LINKS 4 + +struct wmi_ml_partner_info { + u32 vdev_id; + u32 hw_link_id; + u8 addr[ETH_ALEN]; + bool assoc_link; + bool primary_umac; + bool logical_link_idx_valid; + u32 logical_link_idx; +}; + +struct wmi_ml_arg { + bool enabled; + bool assoc_link; + bool mcast_link; + bool link_add; + u8 num_partner_links; + struct wmi_ml_partner_info partner_info[ATH12K_WMI_MLO_MAX_LINKS]; +}; + struct wmi_vdev_start_req_arg { u32 vdev_id; u32 freq; @@ -2900,12 +3075,19 @@ struct wmi_vdev_start_req_arg { u32 mbssid_flags; u32 mbssid_tx_vdev_id; u32 punct_bitmap; + struct wmi_ml_arg ml; }; struct ath12k_wmi_peer_create_arg { const u8 *peer_addr; u32 peer_type; u32 vdev_id; + bool ml_enabled; +}; + +struct wmi_peer_create_mlo_params { + __le32 tlv_header; + __le32 flags; }; struct ath12k_wmi_pdev_set_regdomain_arg { @@ -3075,6 +3257,7 @@ struct wmi_pdev_bss_chan_info_req_cmd { __le32 tlv_header; /* ref wmi_bss_chan_info_req_type */ __le32 req_type; + __le32 pdev_id; } __packed; struct wmi_ap_ps_peer_cmd { @@ -3357,34 +3540,6 @@ struct wmi_bssid_arg { const u8 *bssid; }; -struct wmi_start_scan_arg { - u32 scan_id; - u32 scan_req_id; - u32 vdev_id; - u32 scan_priority; - u32 notify_scan_events; - u32 dwell_time_active; - u32 dwell_time_passive; - u32 min_rest_time; - u32 max_rest_time; - u32 repeat_probe_time; - u32 probe_spacing_time; - u32 idle_time; - u32 max_scan_time; - u32 probe_delay; - u32 scan_ctrl_flags; - - u32 ie_len; - u32 n_channels; - u32 n_ssids; - u32 n_bssids; - - u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; - u32 channels[64]; - struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; - struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; -}; - #define WMI_SCAN_STOP_ONE 0x00000000 #define WMI_SCAN_STOP_VAP_ALL 0x01000000 #define WMI_SCAN_STOP_ALL 0x04000000 @@ -3521,8 +3676,38 @@ struct ath12k_wmi_p2p_noa_info { struct ath12k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS]; } __packed; +#define MAX_WMI_UTF_LEN 252 + +struct ath12k_wmi_ftm_seg_hdr_params { + __le32 len; + __le32 msgref; + __le32 segmentinfo; + __le32 pdev_id; +} __packed; + +struct ath12k_wmi_ftm_cmd { + __le32 tlv_header; + struct ath12k_wmi_ftm_seg_hdr_params seg_hdr; + u8 data[]; +} __packed; + +struct ath12k_wmi_ftm_event { + struct ath12k_wmi_ftm_seg_hdr_params seg_hdr; + u8 data[]; +} __packed; + #define WMI_BEACON_TX_BUFFER_SIZE 512 +#define WMI_EMA_BEACON_CNT GENMASK(7, 0) +#define WMI_EMA_BEACON_IDX GENMASK(15, 8) +#define WMI_EMA_BEACON_FIRST GENMASK(23, 16) +#define WMI_EMA_BEACON_LAST GENMASK(31, 24) + +struct ath12k_wmi_bcn_tmpl_ema_arg { + u8 bcn_cnt; + u8 bcn_index; +}; + struct wmi_bcn_tmpl_cmd { __le32 tlv_header; __le32 vdev_id; @@ -3533,6 +3718,11 @@ struct wmi_bcn_tmpl_cmd { __le32 csa_event_bitmap; __le32 mbssid_ie_offset; __le32 esp_ie_offset; + __le32 csc_switch_count_offset; + __le32 csc_event_bitmap; + __le32 mu_edca_ie_offset; + __le32 feature_enable_bitmap; + __le32 ema_params; } __packed; struct wmi_p2p_go_set_beacon_ie_cmd { @@ -3584,6 +3774,25 @@ struct wmi_vdev_install_key_arg { #define WMI_HECAP_TXRX_MCS_NSS_IDX_160 1 #define WMI_HECAP_TXRX_MCS_NSS_IDX_80_80 2 +#define ATH12K_WMI_MLO_MAX_PARTNER_LINKS \ + (ATH12K_WMI_MLO_MAX_LINKS + ATH12K_MAX_NUM_BRIDGE_LINKS - 1) + +struct peer_assoc_mlo_params { + bool enabled; + bool assoc_link; + bool primary_umac; + bool peer_id_valid; + bool logical_link_idx_valid; + bool bridge_peer; + u8 mld_addr[ETH_ALEN]; + u32 logical_link_idx; + u32 ml_peer_id; + u32 ieee_link_id; + u8 num_partner_links; + struct wmi_ml_partner_info partner_info[ATH12K_WMI_MLO_MAX_LINKS]; + u16 eml_cap; +}; + struct wmi_rate_set_arg { u32 num_rates; u8 rates[WMI_MAX_SUPPORTED_RATES]; @@ -3658,8 +3867,37 @@ struct ath12k_wmi_peer_assoc_arg { u32 peer_eht_tx_mcs_set[WMI_MAX_EHTCAP_RATE_SET]; struct ath12k_wmi_ppe_threshold_arg peer_eht_ppet; u32 punct_bitmap; + bool is_assoc; + struct peer_assoc_mlo_params ml; + bool eht_disable_mcs15; }; +#define ATH12K_WMI_FLAG_MLO_ENABLED BIT(0) +#define ATH12K_WMI_FLAG_MLO_ASSOC_LINK BIT(1) +#define ATH12K_WMI_FLAG_MLO_PRIMARY_UMAC BIT(2) +#define ATH12K_WMI_FLAG_MLO_LINK_ID_VALID BIT(3) +#define ATH12K_WMI_FLAG_MLO_PEER_ID_VALID BIT(4) + +struct wmi_peer_assoc_mlo_partner_info_params { + __le32 tlv_header; + __le32 vdev_id; + __le32 hw_link_id; + __le32 flags; + __le32 logical_link_idx; +} __packed; + +struct wmi_peer_assoc_mlo_params { + __le32 tlv_header; + __le32 flags; + struct ath12k_wmi_mac_addr_params mld_addr; + __le32 logical_link_idx; + __le32 ml_peer_id; + __le32 ieee_link_id; + __le32 emlsr_trans_timeout_us; + __le32 emlsr_trans_delay_us; + __le32 emlsr_padding_delay_us; +} __packed; + struct wmi_peer_assoc_complete_cmd { __le32 tlv_header; struct ath12k_wmi_mac_addr_params peer_macaddr; @@ -3826,6 +4064,28 @@ struct wmi_init_country_cmd { } cc_info; } __packed; +struct wmi_11d_scan_start_arg { + u32 vdev_id; + u32 scan_period_msec; + u32 start_interval_msec; +}; + +struct wmi_11d_scan_start_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 scan_period_msec; + __le32 start_interval_msec; +} __packed; + +struct wmi_11d_scan_stop_cmd { + __le32 tlv_header; + __le32 vdev_id; +} __packed; + +struct wmi_11d_new_cc_event { + __le32 new_alpha2; +} __packed; + struct wmi_delba_send_cmd { __le32 tlv_header; __le32 vdev_id; @@ -3908,8 +4168,17 @@ struct ath12k_wmi_eht_rate_set_params { #define MAX_REG_RULES 10 #define REG_ALPHA2_LEN 2 -#define MAX_6G_REG_RULES 5 -#define REG_US_5G_NUM_REG_RULES 4 +#define MAX_6GHZ_REG_RULES 5 + +struct wmi_set_current_country_arg { + u8 alpha2[REG_ALPHA2_LEN]; +}; + +struct wmi_set_current_country_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 new_alpha2; +} __packed; enum wmi_start_event_param { WMI_VDEV_START_RESP_EVENT = 0, @@ -3930,6 +4199,7 @@ struct wmi_vdev_start_resp_event { }; __le32 cfgd_tx_streams; __le32 cfgd_rx_streams; + __le32 max_allowed_tx_power; } __packed; /* VDEV start response status codes */ @@ -4052,7 +4322,6 @@ struct wmi_vdev_stopped_event { } __packed; struct wmi_pdev_bss_chan_info_event { - __le32 pdev_id; __le32 freq; /* Units in MHz */ __le32 noise_floor; /* units are dBm */ /* rx clear - how often the channel was unused */ @@ -4070,6 +4339,7 @@ struct wmi_pdev_bss_chan_info_event { /*rx_cycle cnt for my bss in 64bits format */ __le32 rx_bss_cycle_count_low; __le32 rx_bss_cycle_count_high; + __le32 pdev_id; } __packed; #define WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS 0 @@ -4214,6 +4484,9 @@ struct wmi_peer_sta_kickout_event { struct ath12k_wmi_mac_addr_params peer_macaddr; } __packed; +#define WMI_ROAM_REASON_MASK GENMASK(3, 0) +#define WMI_ROAM_SUBNET_STATUS_MASK GENMASK(5, 4) + enum wmi_roam_reason { WMI_ROAM_REASON_BETTER_AP = 1, WMI_ROAM_REASON_BEACON_MISS = 2, @@ -4272,6 +4545,7 @@ struct ath12k_wmi_target_cap_arg { }; enum wmi_vdev_type { + WMI_VDEV_TYPE_UNSPEC = 0, WMI_VDEV_TYPE_AP = 1, WMI_VDEV_TYPE_STA = 2, WMI_VDEV_TYPE_IBSS = 3, @@ -4438,6 +4712,7 @@ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_HT, WMI_RATE_PREAMBLE_VHT, WMI_RATE_PREAMBLE_HE, + WMI_RATE_PREAMBLE_EHT, }; /** @@ -4776,10 +5051,9 @@ struct wmi_probe_tmpl_cmd { __le32 buf_len; } __packed; -#define WMI_MAX_MEM_REQS 32 - -#define MAX_RADIOS 3 +#define MAX_RADIOS 2 +#define WMI_MLO_CMD_TIMEOUT_HZ (5 * HZ) #define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ) #define WMI_SEND_TIMEOUT_HZ (3 * HZ) @@ -4789,6 +5063,53 @@ struct ath12k_wmi_pdev { u32 rx_decap_mode; }; +struct ath12k_hw_mode_freq_range_arg { + u32 low_2ghz_freq; + u32 high_2ghz_freq; + u32 low_5ghz_freq; + u32 high_5ghz_freq; +}; + +struct ath12k_svc_ext_mac_phy_info { + enum wmi_host_hw_mode_config_type hw_mode_config_type; + u32 phy_id; + u32 supported_bands; + struct ath12k_hw_mode_freq_range_arg hw_freq_range; +}; + +#define ATH12K_MAX_MAC_PHY_CAP 8 + +struct ath12k_svc_ext_info { + u32 num_hw_modes; + struct ath12k_svc_ext_mac_phy_info mac_phy_info[ATH12K_MAX_MAC_PHY_CAP]; +}; + +/** + * enum ath12k_hw_mode - enum for host mode + * @ATH12K_HW_MODE_SMM: Single mac mode + * @ATH12K_HW_MODE_DBS: DBS mode + * @ATH12K_HW_MODE_SBS: SBS mode with either high share or low share + * @ATH12K_HW_MODE_SBS_UPPER_SHARE: Higher 5 GHz shared with 2.4 GHz + * @ATH12K_HW_MODE_SBS_LOWER_SHARE: Lower 5 GHz shared with 2.4 GHz + * @ATH12K_HW_MODE_MAX: Max, used to indicate invalid mode + */ +enum ath12k_hw_mode { + ATH12K_HW_MODE_SMM, + ATH12K_HW_MODE_DBS, + ATH12K_HW_MODE_SBS, + ATH12K_HW_MODE_SBS_UPPER_SHARE, + ATH12K_HW_MODE_SBS_LOWER_SHARE, + ATH12K_HW_MODE_MAX, +}; + +struct ath12k_hw_mode_info { + bool support_dbs:1; + bool support_sbs:1; + + struct ath12k_hw_mode_freq_range_arg freq_range_caps[ATH12K_HW_MODE_MAX] + [MAX_RADIOS]; +}; + struct ath12k_wmi_base { struct ath12k_base *ab; struct ath12k_wmi_pdev wmi[MAX_RADIOS]; @@ -4806,8 +5127,43 @@ struct ath12k_wmi_base { enum wmi_host_hw_mode_config_type preferred_hw_mode; struct ath12k_wmi_target_cap_arg *targ_cap; + + struct ath12k_svc_ext_info svc_ext_info; + u32 sbs_lower_band_end_freq; + struct ath12k_hw_mode_info hw_mode_info; +}; + +struct wmi_pdev_set_bios_interface_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 param_type_id; + __le32 length; +} __packed; + +enum wmi_bios_param_type { + WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE = 0, + WMI_BIOS_PARAM_TAS_CONFIG_TYPE = 1, + WMI_BIOS_PARAM_TAS_DATA_TYPE = 2, + + /* bandedge control power */ + WMI_BIOS_PARAM_TYPE_BANDEDGE = 3, + + WMI_BIOS_PARAM_TYPE_MAX, }; +struct wmi_pdev_set_bios_sar_table_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 sar_len; + __le32 dbs_backoff_len; +} __packed; + +struct wmi_pdev_set_bios_geo_table_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 geo_len; +} __packed; + #define ATH12K_FW_STATS_BUF_SIZE (1024 * 1024) enum wmi_sys_cap_info_flags { @@ -4845,6 +5201,979 @@ struct wmi_twt_disable_event { __le32 status; } __packed; +struct wmi_mlo_setup_cmd { + __le32 tlv_header; + __le32 mld_group_id; + __le32 pdev_id; +} __packed; + +struct wmi_mlo_setup_arg { + __le32 group_id; + u8 num_partner_links; + u8 *partner_link_id; +}; + +struct wmi_mlo_ready_cmd { + __le32 tlv_header; + __le32 pdev_id; +} __packed; + +enum wmi_mlo_tear_down_reason_code_type { + WMI_MLO_TEARDOWN_SSR_REASON, +}; + +struct wmi_mlo_teardown_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 reason_code; +} __packed; + +struct wmi_mlo_setup_complete_event { + __le32 pdev_id; + __le32 status; +} __packed; + +struct wmi_mlo_teardown_complete_event { + __le32 pdev_id; + __le32 status; +} __packed; + +/* WOW structures */ +enum wmi_wow_wakeup_event { + WOW_BMISS_EVENT = 0, + WOW_BETTER_AP_EVENT, + WOW_DEAUTH_RECVD_EVENT, + WOW_MAGIC_PKT_RECVD_EVENT, + WOW_GTK_ERR_EVENT, + WOW_FOURWAY_HSHAKE_EVENT, + WOW_EAPOL_RECVD_EVENT, + WOW_NLO_DETECTED_EVENT, + WOW_DISASSOC_RECVD_EVENT, + WOW_PATTERN_MATCH_EVENT, + WOW_CSA_IE_EVENT, + WOW_PROBE_REQ_WPS_IE_EVENT, + WOW_AUTH_REQ_EVENT, + WOW_ASSOC_REQ_EVENT, + WOW_HTT_EVENT, + WOW_RA_MATCH_EVENT, + WOW_HOST_AUTO_SHUTDOWN_EVENT, + WOW_IOAC_MAGIC_EVENT, + WOW_IOAC_SHORT_EVENT, + WOW_IOAC_EXTEND_EVENT, + WOW_IOAC_TIMER_EVENT, + WOW_DFS_PHYERR_RADAR_EVENT, + WOW_BEACON_EVENT, + WOW_CLIENT_KICKOUT_EVENT, + WOW_EVENT_MAX, +}; + +enum wmi_wow_interface_cfg { + WOW_IFACE_PAUSE_ENABLED, + WOW_IFACE_PAUSE_DISABLED +}; + +#define C2S(x) case x: return #x + +static inline const char *wow_wakeup_event(enum wmi_wow_wakeup_event ev) +{ + switch (ev) { + C2S(WOW_BMISS_EVENT); + C2S(WOW_BETTER_AP_EVENT); + C2S(WOW_DEAUTH_RECVD_EVENT); + C2S(WOW_MAGIC_PKT_RECVD_EVENT); + C2S(WOW_GTK_ERR_EVENT); + C2S(WOW_FOURWAY_HSHAKE_EVENT); + C2S(WOW_EAPOL_RECVD_EVENT); + C2S(WOW_NLO_DETECTED_EVENT); + C2S(WOW_DISASSOC_RECVD_EVENT); + C2S(WOW_PATTERN_MATCH_EVENT); + C2S(WOW_CSA_IE_EVENT); + C2S(WOW_PROBE_REQ_WPS_IE_EVENT); + C2S(WOW_AUTH_REQ_EVENT); + C2S(WOW_ASSOC_REQ_EVENT); + C2S(WOW_HTT_EVENT); + C2S(WOW_RA_MATCH_EVENT); + C2S(WOW_HOST_AUTO_SHUTDOWN_EVENT); + C2S(WOW_IOAC_MAGIC_EVENT); + C2S(WOW_IOAC_SHORT_EVENT); + C2S(WOW_IOAC_EXTEND_EVENT); + C2S(WOW_IOAC_TIMER_EVENT); + C2S(WOW_DFS_PHYERR_RADAR_EVENT); + C2S(WOW_BEACON_EVENT); + C2S(WOW_CLIENT_KICKOUT_EVENT); + C2S(WOW_EVENT_MAX); + default: + return NULL; + } +} + +enum wmi_wow_wake_reason { + WOW_REASON_UNSPECIFIED = -1, + WOW_REASON_NLOD = 0, + WOW_REASON_AP_ASSOC_LOST, + WOW_REASON_LOW_RSSI, + WOW_REASON_DEAUTH_RECVD, + WOW_REASON_DISASSOC_RECVD, + WOW_REASON_GTK_HS_ERR, + WOW_REASON_EAP_REQ, + WOW_REASON_FOURWAY_HS_RECV, + WOW_REASON_TIMER_INTR_RECV, + WOW_REASON_PATTERN_MATCH_FOUND, + WOW_REASON_RECV_MAGIC_PATTERN, + WOW_REASON_P2P_DISC, + WOW_REASON_WLAN_HB, + WOW_REASON_CSA_EVENT, + WOW_REASON_PROBE_REQ_WPS_IE_RECV, + WOW_REASON_AUTH_REQ_RECV, + WOW_REASON_ASSOC_REQ_RECV, + WOW_REASON_HTT_EVENT, + WOW_REASON_RA_MATCH, + WOW_REASON_HOST_AUTO_SHUTDOWN, + WOW_REASON_IOAC_MAGIC_EVENT, + WOW_REASON_IOAC_SHORT_EVENT, + WOW_REASON_IOAC_EXTEND_EVENT, + WOW_REASON_IOAC_TIMER_EVENT, + WOW_REASON_ROAM_HO, + WOW_REASON_DFS_PHYERR_RADADR_EVENT, + WOW_REASON_BEACON_RECV, + WOW_REASON_CLIENT_KICKOUT_EVENT, + WOW_REASON_PAGE_FAULT = 0x3a, + WOW_REASON_DEBUG_TEST = 0xFF, +}; + +static inline const char *wow_reason(enum wmi_wow_wake_reason reason) +{ + switch (reason) { + C2S(WOW_REASON_UNSPECIFIED); + C2S(WOW_REASON_NLOD); + C2S(WOW_REASON_AP_ASSOC_LOST); + C2S(WOW_REASON_LOW_RSSI); + C2S(WOW_REASON_DEAUTH_RECVD); + C2S(WOW_REASON_DISASSOC_RECVD); + C2S(WOW_REASON_GTK_HS_ERR); + C2S(WOW_REASON_EAP_REQ); + C2S(WOW_REASON_FOURWAY_HS_RECV); + C2S(WOW_REASON_TIMER_INTR_RECV); + C2S(WOW_REASON_PATTERN_MATCH_FOUND); + C2S(WOW_REASON_RECV_MAGIC_PATTERN); + C2S(WOW_REASON_P2P_DISC); + C2S(WOW_REASON_WLAN_HB); + C2S(WOW_REASON_CSA_EVENT); + C2S(WOW_REASON_PROBE_REQ_WPS_IE_RECV); + C2S(WOW_REASON_AUTH_REQ_RECV); + C2S(WOW_REASON_ASSOC_REQ_RECV); + C2S(WOW_REASON_HTT_EVENT); + C2S(WOW_REASON_RA_MATCH); + C2S(WOW_REASON_HOST_AUTO_SHUTDOWN); + C2S(WOW_REASON_IOAC_MAGIC_EVENT); + C2S(WOW_REASON_IOAC_SHORT_EVENT); + C2S(WOW_REASON_IOAC_EXTEND_EVENT); + C2S(WOW_REASON_IOAC_TIMER_EVENT); + C2S(WOW_REASON_ROAM_HO); + C2S(WOW_REASON_DFS_PHYERR_RADADR_EVENT); + C2S(WOW_REASON_BEACON_RECV); + C2S(WOW_REASON_CLIENT_KICKOUT_EVENT); + C2S(WOW_REASON_PAGE_FAULT); + C2S(WOW_REASON_DEBUG_TEST); + default: + return NULL; + } +} + +#undef C2S + +#define WOW_DEFAULT_BITMAP_PATTERN_SIZE 148 +#define WOW_DEFAULT_BITMASK_SIZE 148 + +#define WOW_MIN_PATTERN_SIZE 1 +#define WOW_MAX_PATTERN_SIZE 148 +#define WOW_MAX_PKT_OFFSET 128 +#define WOW_HDR_LEN (sizeof(struct ieee80211_hdr_3addr) + \ + sizeof(struct rfc1042_hdr)) +#define WOW_MAX_REDUCE (WOW_HDR_LEN - sizeof(struct ethhdr) - \ + offsetof(struct ieee80211_hdr_3addr, addr1)) + +struct wmi_wow_bitmap_pattern_params { + __le32 tlv_header; + u8 patternbuf[WOW_DEFAULT_BITMAP_PATTERN_SIZE]; + u8 bitmaskbuf[WOW_DEFAULT_BITMASK_SIZE]; + __le32 pattern_offset; + __le32 pattern_len; + __le32 bitmask_len; + __le32 pattern_id; +} __packed; + +struct wmi_wow_add_pattern_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 pattern_id; + __le32 pattern_type; +} __packed; + +struct wmi_wow_del_pattern_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 pattern_id; + __le32 pattern_type; +} __packed; + +enum wmi_tlv_pattern_type { + WOW_PATTERN_MIN = 0, + WOW_BITMAP_PATTERN = WOW_PATTERN_MIN, + WOW_IPV4_SYNC_PATTERN, + WOW_IPV6_SYNC_PATTERN, + WOW_WILD_CARD_PATTERN, + WOW_TIMER_PATTERN, + WOW_MAGIC_PATTERN, + WOW_IPV6_RA_PATTERN, + WOW_IOAC_PKT_PATTERN, + WOW_IOAC_TMR_PATTERN, + WOW_PATTERN_MAX +}; + +struct wmi_wow_add_del_event_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 is_add; + __le32 event_bitmap; +} __packed; + +struct wmi_wow_enable_cmd { + __le32 tlv_header; + __le32 enable; + __le32 pause_iface_config; + __le32 flags; +} __packed; + +struct wmi_wow_host_wakeup_cmd { + __le32 tlv_header; + __le32 reserved; +} __packed; + +struct wmi_wow_ev_param { + __le32 vdev_id; + __le32 flag; + __le32 wake_reason; + __le32 data_len; +} __packed; + +struct wmi_wow_ev_pg_fault_param { + __le32 len; + u8 data[]; +} __packed; + +struct wmi_wow_ev_arg { + enum wmi_wow_wake_reason wake_reason; +}; + +#define WMI_PNO_MAX_SCHED_SCAN_PLANS 2 +#define WMI_PNO_MAX_SCHED_SCAN_PLAN_INT 7200 +#define WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS 100 +#define WMI_PNO_MAX_NETW_CHANNELS 26 +#define WMI_PNO_MAX_NETW_CHANNELS_EX 60 +#define WMI_PNO_MAX_SUPP_NETWORKS WLAN_SCAN_PARAMS_MAX_SSID +#define WMI_PNO_MAX_IE_LENGTH WLAN_SCAN_PARAMS_MAX_IE_LEN + +/* size based of dot11 declaration without extra IEs as we will not carry those for PNO */ +#define WMI_PNO_MAX_PB_REQ_SIZE 450 + +#define WMI_PNO_24GHZ_DEFAULT_CH 1 +#define WMI_PNO_5GHZ_DEFAULT_CH 36 + +#define WMI_ACTIVE_MAX_CHANNEL_TIME 40 +#define WMI_PASSIVE_MAX_CHANNEL_TIME 110 + +/* SSID broadcast type */ +enum wmi_ssid_bcast_type { + BCAST_UNKNOWN = 0, + BCAST_NORMAL = 1, + BCAST_HIDDEN = 2, +}; + +#define WMI_NLO_MAX_SSIDS 16 +#define WMI_NLO_MAX_CHAN 48 + +#define WMI_NLO_CONFIG_STOP BIT(0) +#define WMI_NLO_CONFIG_START BIT(1) +#define WMI_NLO_CONFIG_RESET BIT(2) +#define WMI_NLO_CONFIG_SLOW_SCAN BIT(4) +#define WMI_NLO_CONFIG_FAST_SCAN BIT(5) +#define WMI_NLO_CONFIG_SSID_HIDE_EN BIT(6) + +/* This bit is used to indicate if EPNO or supplicant PNO is enabled. + * Only one of them can be enabled at a given time + */ +#define WMI_NLO_CONFIG_ENLO BIT(7) +#define WMI_NLO_CONFIG_SCAN_PASSIVE BIT(8) +#define WMI_NLO_CONFIG_ENLO_RESET BIT(9) +#define WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ BIT(10) +#define WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ BIT(11) +#define WMI_NLO_CONFIG_ENABLE_IE_WHITELIST_IN_PROBE_REQ BIT(12) +#define WMI_NLO_CONFIG_ENABLE_CNLO_RSSI_CONFIG BIT(13) + +struct wmi_nlo_ssid_params { + __le32 valid; + struct ath12k_wmi_ssid_params ssid; +} __packed; + +struct wmi_nlo_enc_params { + __le32 valid; + __le32 enc_type; +} __packed; + +struct wmi_nlo_auth_params { + __le32 valid; + __le32 auth_type; +} __packed; + +struct wmi_nlo_bcast_nw_params { + __le32 valid; + __le32 bcast_nw_type; +} __packed; + +struct wmi_nlo_rssi_params { + __le32 valid; + __le32 rssi; +} __packed; + +struct nlo_configured_params { + /* TLV tag and len;*/ + __le32 tlv_header; + struct wmi_nlo_ssid_params ssid; + struct wmi_nlo_enc_params enc_type; + struct wmi_nlo_auth_params auth_type; + struct wmi_nlo_rssi_params rssi_cond; + + /* indicates if the SSID is hidden or not */ + struct wmi_nlo_bcast_nw_params bcast_nw_type; +} __packed; + +struct wmi_network_type_arg { + struct cfg80211_ssid ssid; + u32 authentication; + u32 encryption; + u32 bcast_nw_type; + u8 channel_count; + u16 channels[WMI_PNO_MAX_NETW_CHANNELS_EX]; + s32 rssi_threshold; +}; + +struct wmi_pno_scan_req_arg { + u8 enable; + u8 vdev_id; + u8 uc_networks_count; + struct wmi_network_type_arg a_networks[WMI_PNO_MAX_SUPP_NETWORKS]; + u32 fast_scan_period; + u32 slow_scan_period; + u8 fast_scan_max_cycles; + + bool do_passive_scan; + + u32 delay_start_time; + u32 active_min_time; + u32 active_max_time; + u32 passive_min_time; + u32 passive_max_time; + + /* mac address randomization attributes */ + u32 enable_pno_scan_randomization; + u8 mac_addr[ETH_ALEN]; + u8 mac_addr_mask[ETH_ALEN]; +}; + +struct wmi_wow_nlo_config_cmd { + __le32 tlv_header; + __le32 flags; + __le32 vdev_id; + __le32 fast_scan_max_cycles; + __le32 active_dwell_time; + __le32 passive_dwell_time; + __le32 probe_bundle_size; + + /* ART = IRT */ + __le32 rest_time; + + /* max value that can be reached after scan_backoff_multiplier */ + __le32 max_rest_time; + + __le32 scan_backoff_multiplier; + __le32 fast_scan_period; + + /* specific to windows */ + __le32 slow_scan_period; + + __le32 no_of_ssids; + + __le32 num_of_channels; + + /* NLO scan start delay time in milliseconds */ + __le32 delay_start_time; + + /* MAC Address to use in Probe Req as SA */ + struct ath12k_wmi_mac_addr_params mac_addr; + + /* Mask on which MAC has to be randomized */ + struct ath12k_wmi_mac_addr_params mac_mask; + + /* IE bitmap to use in Probe Req */ + __le32 ie_bitmap[8]; + + /* Number of vendor OUIs. In the TLV vendor_oui[] */ + __le32 num_vendor_oui; + + /* Number of connected NLO band preferences */ + __le32 num_cnlo_band_pref; + + /* The TLVs will follow. + * nlo_configured_params nlo_list[]; + * u32 channel_list[num_of_channels]; + */ +} __packed; + +/* Definition of HW data filtering */ +enum hw_data_filter_type { + WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = BIT(0), + WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = BIT(1), +}; + +struct wmi_hw_data_filter_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 enable; + __le32 hw_filter_bitmap; +} __packed; + +struct wmi_hw_data_filter_arg { + u32 vdev_id; + bool enable; + u32 hw_filter_bitmap; +}; + +#define WMI_IPV6_UC_TYPE 0 +#define WMI_IPV6_AC_TYPE 1 + +#define WMI_IPV6_MAX_COUNT 16 +#define WMI_IPV4_MAX_COUNT 2 + +struct wmi_arp_ns_offload_arg { + u8 ipv4_addr[WMI_IPV4_MAX_COUNT][4]; + u32 ipv4_count; + u32 ipv6_count; + u8 ipv6_addr[WMI_IPV6_MAX_COUNT][16]; + u8 self_ipv6_addr[WMI_IPV6_MAX_COUNT][16]; + u8 ipv6_type[WMI_IPV6_MAX_COUNT]; + bool ipv6_valid[WMI_IPV6_MAX_COUNT]; + u8 mac_addr[ETH_ALEN]; +}; + +#define WMI_MAX_NS_OFFLOADS 2 +#define WMI_MAX_ARP_OFFLOADS 2 + +#define WMI_ARPOL_FLAGS_VALID BIT(0) +#define WMI_ARPOL_FLAGS_MAC_VALID BIT(1) +#define WMI_ARPOL_FLAGS_REMOTE_IP_VALID BIT(2) + +struct wmi_arp_offload_params { + __le32 tlv_header; + __le32 flags; + u8 target_ipaddr[4]; + u8 remote_ipaddr[4]; + struct ath12k_wmi_mac_addr_params target_mac; +} __packed; + +#define WMI_NSOL_FLAGS_VALID BIT(0) +#define WMI_NSOL_FLAGS_MAC_VALID BIT(1) +#define WMI_NSOL_FLAGS_REMOTE_IP_VALID BIT(2) +#define WMI_NSOL_FLAGS_IS_IPV6_ANYCAST BIT(3) + +#define WMI_NSOL_MAX_TARGET_IPS 2 + +struct wmi_ns_offload_params { + __le32 tlv_header; + __le32 flags; + u8 target_ipaddr[WMI_NSOL_MAX_TARGET_IPS][16]; + u8 solicitation_ipaddr[16]; + u8 remote_ipaddr[16]; + struct ath12k_wmi_mac_addr_params target_mac; +} __packed; + +struct wmi_set_arp_ns_offload_cmd { + __le32 tlv_header; + __le32 flags; + __le32 vdev_id; + __le32 num_ns_ext_tuples; + /* The TLVs follow: + * wmi_ns_offload_params ns[WMI_MAX_NS_OFFLOADS]; + * wmi_arp_offload_params arp[WMI_MAX_ARP_OFFLOADS]; + * wmi_ns_offload_params ns_ext[num_ns_ext_tuples]; + */ +} __packed; + +#define GTK_OFFLOAD_OPCODE_MASK 0xFF000000 +#define GTK_OFFLOAD_ENABLE_OPCODE 0x01000000 +#define GTK_OFFLOAD_DISABLE_OPCODE 0x02000000 +#define GTK_OFFLOAD_REQUEST_STATUS_OPCODE 0x04000000 + +#define GTK_OFFLOAD_KEK_BYTES 16 +#define GTK_OFFLOAD_KCK_BYTES 16 +#define GTK_REPLAY_COUNTER_BYTES 8 +#define WMI_MAX_KEY_LEN 32 +#define IGTK_PN_SIZE 6 + +struct wmi_gtk_offload_status_event { + __le32 vdev_id; + __le32 flags; + __le32 refresh_cnt; + __le64 replay_ctr; + u8 igtk_key_index; + u8 igtk_key_length; + u8 igtk_key_rsc[IGTK_PN_SIZE]; + u8 igtk_key[WMI_MAX_KEY_LEN]; + u8 gtk_key_index; + u8 gtk_key_length; + u8 gtk_key_rsc[GTK_REPLAY_COUNTER_BYTES]; + u8 gtk_key[WMI_MAX_KEY_LEN]; +} __packed; + +struct wmi_gtk_rekey_offload_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 flags; + u8 kek[GTK_OFFLOAD_KEK_BYTES]; + u8 kck[GTK_OFFLOAD_KCK_BYTES]; + u8 replay_ctr[GTK_REPLAY_COUNTER_BYTES]; +} __packed; + +struct wmi_sta_keepalive_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 enabled; + + /* WMI_STA_KEEPALIVE_METHOD_ */ + __le32 method; + + /* in seconds */ + __le32 interval; + + /* following this structure is the TLV for struct + * wmi_sta_keepalive_arp_resp_params + */ +} __packed; + +struct wmi_sta_keepalive_arp_resp_params { + __le32 tlv_header; + __le32 src_ip4_addr; + __le32 dest_ip4_addr; + struct ath12k_wmi_mac_addr_params dest_mac_addr; +} __packed; + +struct wmi_sta_keepalive_arg { + u32 vdev_id; + u32 enabled; + u32 method; + u32 interval; + u32 src_ip4_addr; + u32 dest_ip4_addr; + const u8 dest_mac_addr[ETH_ALEN]; +}; + +enum wmi_sta_keepalive_method { + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1, + WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE = 2, + WMI_STA_KEEPALIVE_METHOD_ETHERNET_LOOPBACK = 3, + WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST = 4, + WMI_STA_KEEPALIVE_METHOD_MGMT_VENDOR_ACTION = 5, +}; + +#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 +#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 + +struct wmi_stats_event { + __le32 stats_id; + __le32 num_pdev_stats; + __le32 num_vdev_stats; + __le32 num_peer_stats; + __le32 num_bcnflt_stats; + __le32 num_chan_stats; + __le32 num_mib_stats; + __le32 pdev_id; + __le32 num_bcn_stats; + __le32 num_peer_extd_stats; + __le32 num_peer_extd2_stats; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PDEV_STAT = BIT(2), + WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_BCN_STAT = BIT(11), +}; + +struct wmi_request_stats_cmd { + __le32 tlv_header; + __le32 stats_id; + __le32 vdev_id; + struct ath12k_wmi_mac_addr_params peer_macaddr; + __le32 pdev_id; +} __packed; + +#define WLAN_MAX_AC 4 +#define MAX_TX_RATE_VALUES 10 + +struct wmi_vdev_stats_params { + __le32 vdev_id; + __le32 beacon_snr; + __le32 data_snr; + __le32 num_tx_frames[WLAN_MAX_AC]; + __le32 num_rx_frames; + __le32 num_tx_frames_retries[WLAN_MAX_AC]; + __le32 num_tx_frames_failures[WLAN_MAX_AC]; + __le32 num_rts_fail; + __le32 num_rts_success; + __le32 num_rx_err; + __le32 num_rx_discard; + __le32 num_tx_not_acked; + __le32 tx_rate_history[MAX_TX_RATE_VALUES]; + __le32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +} __packed; + +struct ath12k_wmi_bcn_stats_params { + __le32 vdev_id; + __le32 tx_bcn_succ_cnt; + __le32 tx_bcn_outage_cnt; +} __packed; + +struct ath12k_wmi_pdev_base_stats_params { + a_sle32 chan_nf; + __le32 tx_frame_count; /* Cycles spent transmitting frames */ + __le32 rx_frame_count; /* Cycles spent receiving frames */ + __le32 rx_clear_count; /* Total channel busy time, evidently */ + __le32 cycle_count; /* Total on-channel time */ + __le32 phy_err_count; + __le32 chan_tx_pwr; +} __packed; + +struct ath12k_wmi_pdev_tx_stats_params { + a_sle32 comp_queued; + a_sle32 comp_delivered; + a_sle32 msdu_enqued; + a_sle32 mpdu_enqued; + a_sle32 wmm_drop; + a_sle32 local_enqued; + a_sle32 local_freed; + a_sle32 hw_queued; + a_sle32 hw_reaped; + a_sle32 underrun; + a_sle32 tx_abort; + a_sle32 mpdus_requed; + __le32 tx_ko; + __le32 data_rc; + __le32 self_triggers; + __le32 sw_retry_failure; + __le32 illgl_rate_phy_err; + __le32 pdev_cont_xretry; + __le32 pdev_tx_timeout; + __le32 pdev_resets; + __le32 stateless_tid_alloc_failure; + __le32 phy_underrun; + __le32 txop_ovf; +} __packed; + +struct ath12k_wmi_pdev_rx_stats_params { + a_sle32 mid_ppdu_route_change; + a_sle32 status_rcvd; + a_sle32 r0_frags; + a_sle32 r1_frags; + a_sle32 r2_frags; + a_sle32 r3_frags; + a_sle32 htt_msdus; + a_sle32 htt_mpdus; + a_sle32 loc_msdus; + a_sle32 loc_mpdus; + a_sle32 oversize_amsdu; + a_sle32 phy_errs; + a_sle32 phy_err_drop; + a_sle32 mpdu_errs; +} __packed; + +struct ath12k_wmi_pdev_stats_params { + struct ath12k_wmi_pdev_base_stats_params base; + struct ath12k_wmi_pdev_tx_stats_params tx; + struct ath12k_wmi_pdev_rx_stats_params rx; +} __packed; + +struct ath12k_fw_stats_req_params { + u32 stats_id; + u32 vdev_id; + u32 pdev_id; +}; + +#define WMI_REQ_CTRL_PATH_PDEV_TX_STAT 1 +#define WMI_REQUEST_CTRL_PATH_STAT_GET 1 + +#define WMI_TPC_CONFIG BIT(1) +#define WMI_TPC_REG_PWR_ALLOWED BIT(2) +#define WMI_TPC_RATES_ARRAY1 BIT(3) +#define WMI_TPC_RATES_ARRAY2 BIT(4) +#define WMI_TPC_RATES_DL_OFDMA_ARRAY BIT(5) +#define WMI_TPC_CTL_PWR_ARRAY BIT(6) +#define WMI_TPC_CONFIG_PARAM 0x1 +#define ATH12K_TPC_RATE_ARRAY_MU GENMASK(15, 8) +#define ATH12K_TPC_RATE_ARRAY_SU GENMASK(7, 0) +#define TPC_STATS_REG_PWR_ALLOWED_TYPE 0 + +enum wmi_halphy_ctrl_path_stats_id { + WMI_HALPHY_PDEV_TX_SU_STATS = 0, + WMI_HALPHY_PDEV_TX_SUTXBF_STATS, + WMI_HALPHY_PDEV_TX_MU_STATS, + WMI_HALPHY_PDEV_TX_MUTXBF_STATS, + WMI_HALPHY_PDEV_TX_STATS_MAX, +}; + +enum ath12k_wmi_tpc_stats_rates_array { + ATH12K_TPC_STATS_RATES_ARRAY1, + ATH12K_TPC_STATS_RATES_ARRAY2, +}; + +enum ath12k_wmi_tpc_stats_ctl_array { + ATH12K_TPC_STATS_CTL_ARRAY, + ATH12K_TPC_STATS_CTL_160ARRAY, +}; + +enum ath12k_wmi_tpc_stats_events { + ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT, + ATH12K_TPC_STATS_RATES_EVENT1, + ATH12K_TPC_STATS_RATES_EVENT2, + ATH12K_TPC_STATS_CTL_TABLE_EVENT +}; + +struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params { + __le32 tlv_header; + __le32 stats_id_mask; + __le32 request_id; + __le32 action; + __le32 subid; +} __packed; + +struct ath12k_wmi_pdev_tpc_stats_event_fixed_params { + __le32 pdev_id; + __le32 end_of_event; + __le32 event_count; +} __packed; + +struct wmi_tpc_config_params { + __le32 reg_domain; + __le32 chan_freq; + __le32 phy_mode; + __le32 twice_antenna_reduction; + __le32 twice_max_reg_power; + __le32 twice_antenna_gain; + __le32 power_limit; + __le32 rate_max; + __le32 num_tx_chain; + __le32 ctl; + __le32 flags; + __le32 caps; +} __packed; + +struct wmi_max_reg_power_fixed_params { + __le32 reg_power_type; + __le32 reg_array_len; + __le32 d1; + __le32 d2; + __le32 d3; + __le32 d4; +} __packed; + +struct wmi_max_reg_power_allowed_arg { + struct wmi_max_reg_power_fixed_params tpc_reg_pwr; + s16 *reg_pwr_array; +}; + +struct wmi_tpc_rates_array_fixed_params { + __le32 rate_array_type; + __le32 rate_array_len; +} __packed; + +struct wmi_tpc_rates_array_arg { + struct wmi_tpc_rates_array_fixed_params tpc_rates_array; + s16 *rate_array; +}; + +struct wmi_tpc_ctl_pwr_fixed_params { + __le32 ctl_array_type; + __le32 ctl_array_len; + __le32 end_of_ctl_pwr; + __le32 ctl_pwr_count; + __le32 d1; + __le32 d2; + __le32 d3; + __le32 d4; +} __packed; + +struct wmi_tpc_ctl_pwr_table_arg { + struct wmi_tpc_ctl_pwr_fixed_params tpc_ctl_pwr; + s8 *ctl_pwr_table; +}; + +struct wmi_tpc_stats_arg { + u32 pdev_id; + u32 event_count; + u32 end_of_event; + u32 tlvs_rcvd; + struct wmi_max_reg_power_allowed_arg max_reg_allowed_power; + struct wmi_tpc_rates_array_arg rates_array1; + struct wmi_tpc_rates_array_arg rates_array2; + struct wmi_tpc_config_params tpc_config; + struct wmi_tpc_ctl_pwr_table_arg ctl_array; +}; + +struct wmi_vdev_ch_power_params { + __le32 tlv_header; + + /* Channel center frequency (MHz) */ + __le32 chan_cfreq; + + /* Unit: dBm, either PSD/EIRP power for this frequency or + * incremental for non-PSD BW + */ + __le32 tx_power; +} __packed; + +struct wmi_vdev_set_tpc_power_cmd { + __le32 tlv_header; + __le32 vdev_id; + + /* Value: 0 or 1, is PSD power or not */ + __le32 psd_power; + + /* Maximum EIRP power (dBm units), valid only if power is PSD */ + __le32 eirp_power; + + /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */ + __le32 power_type_6ghz; + + /* This fixed_param TLV is followed by the below TLVs: + * num_pwr_levels of wmi_vdev_ch_power_info + * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks). + * For non-PSD power, the power values are for 20, 40, and till + * BSS BW power levels. + * The num_pwr_levels will be checked by sw how many elements present + * in the variable-length array. + */ +} __packed; + +#define CRTL_F_DYNC_FORCE_LINK_NUM GENMASK(3, 2) + +struct wmi_mlo_link_set_active_cmd { + __le32 tlv_header; + __le32 force_mode; + __le32 reason; + __le32 use_ieee_link_id_bitmap; + struct ath12k_wmi_mac_addr_params ap_mld_mac_addr; + __le32 ctrl_flags; +} __packed; + +struct wmi_mlo_set_active_link_number_params { + __le32 tlv_header; + __le32 num_of_link; + __le32 vdev_type; + __le32 vdev_subtype; + __le32 home_freq; +} __packed; + +#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_1 GENMASK(7, 0) +#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_2 GENMASK(15, 8) +#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_3 GENMASK(23, 16) +#define WMI_DISALW_MLO_MODE_BMAP_IEEE_LINK_ID_COMB_4 GENMASK(31, 24) + +struct wmi_disallowed_mlo_mode_bitmap_params { + __le32 tlv_header; + __le32 disallowed_mode_bitmap; + __le32 ieee_link_id_comb; +} __packed; + +enum wmi_mlo_link_force_mode { + WMI_MLO_LINK_FORCE_MODE_ACTIVE = 1, + WMI_MLO_LINK_FORCE_MODE_INACTIVE = 2, + WMI_MLO_LINK_FORCE_MODE_ACTIVE_LINK_NUM = 3, + WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM = 4, + WMI_MLO_LINK_FORCE_MODE_NO_FORCE = 5, + WMI_MLO_LINK_FORCE_MODE_ACTIVE_INACTIVE = 6, + WMI_MLO_LINK_FORCE_MODE_NON_FORCE_UPDATE = 7, +}; + +enum wmi_mlo_link_force_reason { + WMI_MLO_LINK_FORCE_REASON_NEW_CONNECT = 1, + WMI_MLO_LINK_FORCE_REASON_NEW_DISCONNECT = 2, + WMI_MLO_LINK_FORCE_REASON_LINK_REMOVAL = 3, + WMI_MLO_LINK_FORCE_REASON_TDLS = 4, + WMI_MLO_LINK_FORCE_REASON_REVERT_FAILURE = 5, + WMI_MLO_LINK_FORCE_REASON_LINK_DELETE = 6, + WMI_MLO_LINK_FORCE_REASON_SINGLE_LINK_EMLSR_OP = 7, +}; + +struct wmi_mlo_link_num_arg { + u32 num_of_link; + u32 vdev_type; + u32 vdev_subtype; + u32 home_freq; +}; + +struct wmi_mlo_control_flags_arg { + bool overwrite_force_active_bitmap; + bool overwrite_force_inactive_bitmap; + bool dync_force_link_num; + bool post_re_evaluate; + u8 post_re_evaluate_loops; + bool dont_reschedule_workqueue; +}; + +struct wmi_ml_link_force_cmd_arg { + u8 ap_mld_mac_addr[ETH_ALEN]; + u16 ieee_link_id_bitmap; + u16 ieee_link_id_bitmap2; + u8 link_num; +}; + +struct wmi_ml_disallow_mode_bmap_arg { + u32 disallowed_mode; + union { + u32 ieee_link_id_comb; + u8 ieee_link_id[4]; + }; +}; + +/* maximum size of link number param array + * for MLO link set active command + */ +#define WMI_MLO_LINK_NUM_SZ 2 + +/* maximum size of vdev bitmap array for + * MLO link set active command + */ +#define WMI_MLO_VDEV_BITMAP_SZ 2 + +/* Max number of disallowed bitmap combination + * sent to firmware + */ +#define WMI_ML_MAX_DISALLOW_BMAP_COMB 4 + +struct wmi_mlo_link_set_active_arg { + enum wmi_mlo_link_force_mode force_mode; + enum wmi_mlo_link_force_reason reason; + u32 num_link_entry; + u32 num_vdev_bitmap; + u32 num_inactive_vdev_bitmap; + struct wmi_mlo_link_num_arg link_num[WMI_MLO_LINK_NUM_SZ]; + u32 vdev_bitmap[WMI_MLO_VDEV_BITMAP_SZ]; + u32 inactive_vdev_bitmap[WMI_MLO_VDEV_BITMAP_SZ]; + struct wmi_mlo_control_flags_arg ctrl_flags; + bool use_ieee_link_id; + struct wmi_ml_link_force_cmd_arg force_cmd; + u32 num_disallow_mode_comb; + struct wmi_ml_disallow_mode_bmap_arg disallow_bmap[WMI_ML_MAX_DISALLOW_BMAP_COMB]; +}; + 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, @@ -4856,12 +6185,12 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id, struct sk_buff *frame); int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id, const u8 *p2p_ie); -int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, +int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, struct ieee80211_mutable_offsets *offs, - struct sk_buff *bcn); + struct sk_buff *bcn, + struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args); int ath12k_wmi_vdev_down(struct ath12k *ar, u8 vdev_id); -int ath12k_wmi_vdev_up(struct ath12k *ar, u32 vdev_id, u32 aid, - const u8 *bssid); +int ath12k_wmi_vdev_up(struct ath12k *ar, struct ath12k_wmi_vdev_up_params *params); int ath12k_wmi_vdev_stop(struct ath12k *ar, u8 vdev_id); int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, bool restart); @@ -4931,11 +6260,17 @@ int ath12k_wmi_send_bcn_offload_control_cmd(struct ath12k *ar, u32 vdev_id, u32 bcn_ctrl_op); int ath12k_wmi_send_init_country_cmd(struct ath12k *ar, struct ath12k_wmi_init_country_arg *arg); +int +ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, + struct wmi_set_current_country_arg *arg); int ath12k_wmi_peer_rx_reorder_queue_setup(struct ath12k *ar, int vdev_id, const u8 *addr, dma_addr_t paddr, u8 tid, u8 ba_window_size_valid, u32 ba_window_size); +int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, + struct wmi_11d_scan_start_arg *arg); +int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id); int ath12k_wmi_rx_reord_queue_remove(struct ath12k *ar, struct ath12k_wmi_rx_reorder_queue_remove_arg *arg); @@ -4966,6 +6301,17 @@ int ath12k_wmi_probe_resp_tmpl(struct ath12k *ar, u32 vdev_id, struct sk_buff *tmpl); int ath12k_wmi_set_hw_mode(struct ath12k_base *ab, enum wmi_host_hw_mode_config_type mode); +int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, + const u8 *buf, size_t buf_len); +int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table); +int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table); +int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, + u32 vdev_id, u32 pdev_id); +__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); + +int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type); +void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) @@ -4993,4 +6339,39 @@ ath12k_wmi_mac_phy_get_hw_link_id(const struct ath12k_wmi_mac_phy_caps_params *p WMI_CAPS_PARAMS_HW_LINK_ID); } +int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar); +int ath12k_wmi_wow_enable(struct ath12k *ar); +int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id); +int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, + const u8 *pattern, const u8 *mask, + int pattern_len, int pattern_offset); +int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, + enum wmi_wow_wakeup_event event, + u32 enable); +int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, + struct wmi_pno_scan_req_arg *pno_scan); +int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, + struct wmi_hw_data_filter_arg *arg); +int ath12k_wmi_arp_ns_offload(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct wmi_arp_ns_offload_arg *offload, + bool enable); +int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, + struct ath12k_link_vif *arvif, bool enable); +int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, + struct ath12k_link_vif *arvif); +int ath12k_wmi_sta_keepalive(struct ath12k *ar, + const struct wmi_sta_keepalive_arg *arg); +int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params); +int ath12k_wmi_mlo_ready(struct ath12k *ar); +int ath12k_wmi_mlo_teardown(struct ath12k *ar); +void ath12k_wmi_fw_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, u32 stats_id, + char *buf); +bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar); +int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, + u32 vdev_id, + struct ath12k_reg_tpc_power_info *param); +int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, + struct wmi_mlo_link_set_active_arg *param); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c new file mode 100644 index 000000000000..dce9bd0bcaef --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -0,0 +1,1028 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/inetdevice.h> +#include <net/addrconf.h> +#include <net/if_inet6.h> +#include <net/ipv6.h> + +#include "mac.h" + +#include <net/mac80211.h> +#include "core.h" +#include "hif.h" +#include "debug.h" +#include "wmi.h" +#include "wow.h" + +static const struct wiphy_wowlan_support ath12k_wowlan_support = { + .flags = WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + .pattern_min_len = WOW_MIN_PATTERN_SIZE, + .pattern_max_len = WOW_MAX_PATTERN_SIZE, + .max_pkt_offset = WOW_MAX_PKT_OFFSET, +}; + +static inline bool ath12k_wow_is_p2p_vdev(struct ath12k_vif *ahvif) +{ + return (ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_P2P_DEVICE || + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_P2P_CLIENT || + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_P2P_GO); +} + +int ath12k_wow_enable(struct ath12k *ar) +{ + struct ath12k_base *ab = ar->ab; + int i, ret; + + clear_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); + + /* The firmware might be busy and it can not enter WoW immediately. + * In that case firmware notifies host with + * ATH12K_HTC_MSG_NACK_SUSPEND message, asking host to try again + * later. Per the firmware team there could be up to 10 loops. + */ + for (i = 0; i < ATH12K_WOW_RETRY_NUM; i++) { + reinit_completion(&ab->htc_suspend); + + ret = ath12k_wmi_wow_enable(ar); + if (ret) { + ath12k_warn(ab, "failed to issue wow enable: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ); + if (ret == 0) { + ath12k_warn(ab, + "timed out while waiting for htc suspend completion\n"); + return -ETIMEDOUT; + } + + if (test_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags)) + /* success, suspend complete received */ + return 0; + + ath12k_warn(ab, "htc suspend not complete, retrying (try %d)\n", + i); + msleep(ATH12K_WOW_RETRY_WAIT_MS); + } + + ath12k_warn(ab, "htc suspend not complete, failing after %d tries\n", i); + + return -ETIMEDOUT; +} + +int ath12k_wow_wakeup(struct ath12k *ar) +{ + struct ath12k_base *ab = ar->ab; + int ret; + + reinit_completion(&ab->wow.wakeup_completed); + + ret = ath12k_wmi_wow_host_wakeup_ind(ar); + if (ret) { + ath12k_warn(ab, "failed to send wow wakeup indication: %d\n", + ret); + return ret; + } + + ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ); + if (ret == 0) { + ath12k_warn(ab, "timed out while waiting for wow wakeup completion\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ath12k_wow_vif_cleanup(struct ath12k_link_vif *arvif) +{ + struct ath12k *ar = arvif->ar; + int i, ret; + + for (i = 0; i < WOW_EVENT_MAX; i++) { + ret = ath12k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); + if (ret) { + ath12k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n", + wow_wakeup_event(i), arvif->vdev_id, ret); + return ret; + } + } + + for (i = 0; i < ar->wow.max_num_patterns; i++) { + ret = ath12k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); + if (ret) { + ath12k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n", + i, arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_cleanup(struct ath12k *ar) +{ + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath12k_wow_vif_cleanup(arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +/* Convert a 802.3 format to a 802.11 format. + * +------------+-----------+--------+----------------+ + * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | + * +------------+-----------+--------+----------------+ + * |__ |_______ |____________ |________ + * | | | | + * +--+------------+----+-----------+---------------+-----------+ + * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | + * +--+------------+----+-----------+---------------+-----------+ + */ +static void +ath12k_wow_convert_8023_to_80211(struct ath12k *ar, + const struct cfg80211_pkt_pattern *eth_pattern, + struct ath12k_pkt_pattern *i80211_pattern) +{ + size_t r1042_eth_ofs = offsetof(struct rfc1042_hdr, eth_type); + size_t a1_ofs = offsetof(struct ieee80211_hdr_3addr, addr1); + size_t a3_ofs = offsetof(struct ieee80211_hdr_3addr, addr3); + size_t i80211_hdr_len = sizeof(struct ieee80211_hdr_3addr); + size_t prot_ofs = offsetof(struct ethhdr, h_proto); + size_t src_ofs = offsetof(struct ethhdr, h_source); + u8 eth_bytemask[WOW_MAX_PATTERN_SIZE] = {}; + const u8 *eth_pat = eth_pattern->pattern; + size_t eth_pat_len = eth_pattern->pattern_len; + size_t eth_pkt_ofs = eth_pattern->pkt_offset; + u8 *bytemask = i80211_pattern->bytemask; + u8 *pat = i80211_pattern->pattern; + size_t pat_len = 0; + size_t pkt_ofs = 0; + size_t delta; + int i; + + /* convert bitmask to bytemask */ + for (i = 0; i < eth_pat_len; i++) + if (eth_pattern->mask[i / 8] & BIT(i % 8)) + eth_bytemask[i] = 0xff; + + if (eth_pkt_ofs < ETH_ALEN) { + pkt_ofs = eth_pkt_ofs + a1_ofs; + + if (size_add(eth_pkt_ofs, eth_pat_len) < ETH_ALEN) { + memcpy(pat, eth_pat, eth_pat_len); + memcpy(bytemask, eth_bytemask, eth_pat_len); + + pat_len = eth_pat_len; + } else if (size_add(eth_pkt_ofs, eth_pat_len) < prot_ofs) { + memcpy(pat, eth_pat, ETH_ALEN - eth_pkt_ofs); + memcpy(bytemask, eth_bytemask, ETH_ALEN - eth_pkt_ofs); + + delta = eth_pkt_ofs + eth_pat_len - src_ofs; + memcpy(pat + a3_ofs - pkt_ofs, + eth_pat + ETH_ALEN - eth_pkt_ofs, + delta); + memcpy(bytemask + a3_ofs - pkt_ofs, + eth_bytemask + ETH_ALEN - eth_pkt_ofs, + delta); + + pat_len = a3_ofs - pkt_ofs + delta; + } else { + memcpy(pat, eth_pat, ETH_ALEN - eth_pkt_ofs); + memcpy(bytemask, eth_bytemask, ETH_ALEN - eth_pkt_ofs); + + memcpy(pat + a3_ofs - pkt_ofs, + eth_pat + ETH_ALEN - eth_pkt_ofs, + ETH_ALEN); + memcpy(bytemask + a3_ofs - pkt_ofs, + eth_bytemask + ETH_ALEN - eth_pkt_ofs, + ETH_ALEN); + + delta = eth_pkt_ofs + eth_pat_len - prot_ofs; + memcpy(pat + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_pat + prot_ofs - eth_pkt_ofs, + delta); + memcpy(bytemask + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_bytemask + prot_ofs - eth_pkt_ofs, + delta); + + pat_len = i80211_hdr_len + r1042_eth_ofs - pkt_ofs + delta; + } + } else if (eth_pkt_ofs < prot_ofs) { + pkt_ofs = eth_pkt_ofs - ETH_ALEN + a3_ofs; + + if (size_add(eth_pkt_ofs, eth_pat_len) < prot_ofs) { + memcpy(pat, eth_pat, eth_pat_len); + memcpy(bytemask, eth_bytemask, eth_pat_len); + + pat_len = eth_pat_len; + } else { + memcpy(pat, eth_pat, prot_ofs - eth_pkt_ofs); + memcpy(bytemask, eth_bytemask, prot_ofs - eth_pkt_ofs); + + delta = eth_pkt_ofs + eth_pat_len - prot_ofs; + memcpy(pat + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_pat + prot_ofs - eth_pkt_ofs, + delta); + memcpy(bytemask + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_bytemask + prot_ofs - eth_pkt_ofs, + delta); + + pat_len = i80211_hdr_len + r1042_eth_ofs - pkt_ofs + delta; + } + } else { + pkt_ofs = eth_pkt_ofs - prot_ofs + i80211_hdr_len + r1042_eth_ofs; + + memcpy(pat, eth_pat, eth_pat_len); + memcpy(bytemask, eth_bytemask, eth_pat_len); + + pat_len = eth_pat_len; + } + + i80211_pattern->pattern_len = pat_len; + i80211_pattern->pkt_offset = pkt_ofs; +} + +static int +ath12k_wow_pno_check_and_convert(struct ath12k *ar, u32 vdev_id, + const struct cfg80211_sched_scan_request *nd_config, + struct wmi_pno_scan_req_arg *pno) +{ + int i, j; + u8 ssid_len; + + pno->enable = 1; + pno->vdev_id = vdev_id; + pno->uc_networks_count = nd_config->n_match_sets; + + if (!pno->uc_networks_count || + pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) + return -EINVAL; + + if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) + return -EINVAL; + + /* Filling per profile params */ + for (i = 0; i < pno->uc_networks_count; i++) { + ssid_len = nd_config->match_sets[i].ssid.ssid_len; + + if (ssid_len == 0 || ssid_len > 32) + return -EINVAL; + + pno->a_networks[i].ssid.ssid_len = ssid_len; + + memcpy(pno->a_networks[i].ssid.ssid, + nd_config->match_sets[i].ssid.ssid, + ssid_len); + pno->a_networks[i].authentication = 0; + pno->a_networks[i].encryption = 0; + pno->a_networks[i].bcast_nw_type = 0; + + /* Copying list of valid channel into request */ + pno->a_networks[i].channel_count = nd_config->n_channels; + pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; + + for (j = 0; j < nd_config->n_channels; j++) { + pno->a_networks[i].channels[j] = + nd_config->channels[j]->center_freq; + } + } + + /* set scan to passive if no SSIDs are specified in the request */ + if (nd_config->n_ssids == 0) + pno->do_passive_scan = true; + else + pno->do_passive_scan = false; + + for (i = 0; i < nd_config->n_ssids; i++) { + for (j = 0; j < pno->uc_networks_count; j++) { + if (pno->a_networks[j].ssid.ssid_len == + nd_config->ssids[i].ssid_len && + !memcmp(pno->a_networks[j].ssid.ssid, + nd_config->ssids[i].ssid, + pno->a_networks[j].ssid.ssid_len)) { + pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; + break; + } + } + } + + if (nd_config->n_scan_plans == 2) { + pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; + pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; + pno->slow_scan_period = + nd_config->scan_plans[1].interval * MSEC_PER_SEC; + } else if (nd_config->n_scan_plans == 1) { + pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; + pno->fast_scan_max_cycles = 1; + pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; + } else { + ath12k_warn(ar->ab, "Invalid number of PNO scan plans: %d", + nd_config->n_scan_plans); + } + + if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + /* enable mac randomization */ + pno->enable_pno_scan_randomization = 1; + memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); + memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); + } + + pno->delay_start_time = nd_config->delay; + + /* Current FW does not support min-max range for dwell time */ + pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; + pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; + + return 0; +} + +static int ath12k_wow_vif_set_wakeups(struct ath12k_link_vif *arvif, + struct cfg80211_wowlan *wowlan) +{ + const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; + struct ath12k *ar = arvif->ar; + unsigned long wow_mask = 0; + int pattern_id = 0; + int ret, i, j; + + /* Setup requested WOW features */ + switch (arvif->ahvif->vdev_type) { + case WMI_VDEV_TYPE_IBSS: + __set_bit(WOW_BEACON_EVENT, &wow_mask); + fallthrough; + case WMI_VDEV_TYPE_AP: + __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); + __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); + __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); + __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); + __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); + __set_bit(WOW_HTT_EVENT, &wow_mask); + __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); + break; + case WMI_VDEV_TYPE_STA: + if (wowlan->disconnect) { + __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); + __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); + __set_bit(WOW_BMISS_EVENT, &wow_mask); + __set_bit(WOW_CSA_IE_EVENT, &wow_mask); + } + + if (wowlan->magic_pkt) + __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); + + if (wowlan->nd_config) { + struct wmi_pno_scan_req_arg *pno; + int ret; + + pno = kzalloc(sizeof(*pno), GFP_KERNEL); + if (!pno) + return -ENOMEM; + + ar->nlo_enabled = true; + + ret = ath12k_wow_pno_check_and_convert(ar, arvif->vdev_id, + wowlan->nd_config, pno); + if (!ret) { + ath12k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); + __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); + } + + kfree(pno); + } + break; + default: + break; + } + + for (i = 0; i < wowlan->n_patterns; i++) { + const struct cfg80211_pkt_pattern *eth_pattern = &patterns[i]; + struct ath12k_pkt_pattern new_pattern = {}; + + if (WARN_ON(eth_pattern->pattern_len > WOW_MAX_PATTERN_SIZE)) + return -EINVAL; + + if (ar->ab->wow.wmi_conf_rx_decap_mode == + ATH12K_HW_TXRX_NATIVE_WIFI) { + ath12k_wow_convert_8023_to_80211(ar, eth_pattern, + &new_pattern); + + if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) + return -EINVAL; + } else { + memcpy(new_pattern.pattern, eth_pattern->pattern, + eth_pattern->pattern_len); + + /* convert bitmask to bytemask */ + for (j = 0; j < eth_pattern->pattern_len; j++) + if (eth_pattern->mask[j / 8] & BIT(j % 8)) + new_pattern.bytemask[j] = 0xff; + + new_pattern.pattern_len = eth_pattern->pattern_len; + new_pattern.pkt_offset = eth_pattern->pkt_offset; + } + + ret = ath12k_wmi_wow_add_pattern(ar, arvif->vdev_id, + pattern_id, + new_pattern.pattern, + new_pattern.bytemask, + new_pattern.pattern_len, + new_pattern.pkt_offset); + if (ret) { + ath12k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n", + pattern_id, + arvif->vdev_id, ret); + return ret; + } + + pattern_id++; + __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); + } + + for (i = 0; i < WOW_EVENT_MAX; i++) { + if (!test_bit(i, &wow_mask)) + continue; + ret = ath12k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); + if (ret) { + ath12k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n", + wow_wakeup_event(i), arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_set_wakeups(struct ath12k *ar, + struct cfg80211_wowlan *wowlan) +{ + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (ath12k_wow_is_p2p_vdev(arvif->ahvif)) + continue; + ret = ath12k_wow_vif_set_wakeups(arvif, wowlan); + if (ret) { + ath12k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_vdev_clean_nlo(struct ath12k *ar, u32 vdev_id) +{ + struct wmi_pno_scan_req_arg *pno; + int ret; + + if (!ar->nlo_enabled) + return 0; + + pno = kzalloc(sizeof(*pno), GFP_KERNEL); + if (!pno) + return -ENOMEM; + + pno->enable = 0; + ret = ath12k_wmi_wow_config_pno(ar, vdev_id, pno); + if (ret) { + ath12k_warn(ar->ab, "failed to disable PNO: %d", ret); + goto out; + } + + ar->nlo_enabled = false; + +out: + kfree(pno); + return ret; +} + +static int ath12k_wow_vif_clean_nlo(struct ath12k_link_vif *arvif) +{ + struct ath12k *ar = arvif->ar; + + switch (arvif->ahvif->vdev_type) { + case WMI_VDEV_TYPE_STA: + return ath12k_wow_vdev_clean_nlo(ar, arvif->vdev_id); + default: + return 0; + } +} + +static int ath12k_wow_nlo_cleanup(struct ath12k *ar) +{ + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (ath12k_wow_is_p2p_vdev(arvif->ahvif)) + continue; + + ret = ath12k_wow_vif_clean_nlo(arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_set_hw_filter(struct ath12k *ar) +{ + struct wmi_hw_data_filter_arg arg; + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->ahvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + arg.vdev_id = arvif->vdev_id; + arg.enable = true; + arg.hw_filter_bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC; + ret = ath12k_wmi_hw_data_filter_cmd(ar, &arg); + if (ret) { + ath12k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_clear_hw_filter(struct ath12k *ar) +{ + struct wmi_hw_data_filter_arg arg; + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->ahvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + arg.vdev_id = arvif->vdev_id; + arg.enable = false; + arg.hw_filter_bitmap = 0; + ret = ath12k_wmi_hw_data_filter_cmd(ar, &arg); + + if (ret) { + ath12k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static void ath12k_wow_generate_ns_mc_addr(struct ath12k_base *ab, + struct wmi_arp_ns_offload_arg *offload) +{ + int i; + + for (i = 0; i < offload->ipv6_count; i++) { + offload->self_ipv6_addr[i][0] = 0xff; + offload->self_ipv6_addr[i][1] = 0x02; + offload->self_ipv6_addr[i][11] = 0x01; + offload->self_ipv6_addr[i][12] = 0xff; + offload->self_ipv6_addr[i][13] = + offload->ipv6_addr[i][13]; + offload->self_ipv6_addr[i][14] = + offload->ipv6_addr[i][14]; + offload->self_ipv6_addr[i][15] = + offload->ipv6_addr[i][15]; + ath12k_dbg(ab, ATH12K_DBG_WOW, "NS solicited addr %pI6\n", + offload->self_ipv6_addr[i]); + } +} + +static void ath12k_wow_prepare_ns_offload(struct ath12k_link_vif *arvif, + struct wmi_arp_ns_offload_arg *offload) +{ + struct net_device *ndev = ieee80211_vif_to_wdev(arvif->ahvif->vif)->netdev; + struct ath12k_base *ab = arvif->ar->ab; + struct inet6_ifaddr *ifa6; + struct ifacaddr6 *ifaca6; + struct inet6_dev *idev; + u32 count = 0, scope; + + if (!ndev) + return; + + idev = in6_dev_get(ndev); + if (!idev) + return; + + ath12k_dbg(ab, ATH12K_DBG_WOW, "wow prepare ns offload\n"); + + read_lock_bh(&idev->lock); + + /* get unicast address */ + list_for_each_entry(ifa6, &idev->addr_list, if_list) { + if (count >= WMI_IPV6_MAX_COUNT) + goto unlock; + + if (ifa6->flags & IFA_F_DADFAILED) + continue; + + scope = ipv6_addr_src_scope(&ifa6->addr); + if (scope != IPV6_ADDR_SCOPE_LINKLOCAL && + scope != IPV6_ADDR_SCOPE_GLOBAL) { + ath12k_dbg(ab, ATH12K_DBG_WOW, + "Unsupported ipv6 scope: %d\n", scope); + continue; + } + + memcpy(offload->ipv6_addr[count], &ifa6->addr.s6_addr, + sizeof(ifa6->addr.s6_addr)); + offload->ipv6_type[count] = WMI_IPV6_UC_TYPE; + ath12k_dbg(ab, ATH12K_DBG_WOW, "mac count %d ipv6 uc %pI6 scope %d\n", + count, offload->ipv6_addr[count], + scope); + count++; + } + + /* get anycast address */ + rcu_read_lock(); + + for (ifaca6 = rcu_dereference(idev->ac_list); ifaca6; + ifaca6 = rcu_dereference(ifaca6->aca_next)) { + if (count >= WMI_IPV6_MAX_COUNT) { + rcu_read_unlock(); + goto unlock; + } + + scope = ipv6_addr_src_scope(&ifaca6->aca_addr); + if (scope != IPV6_ADDR_SCOPE_LINKLOCAL && + scope != IPV6_ADDR_SCOPE_GLOBAL) { + ath12k_dbg(ab, ATH12K_DBG_WOW, + "Unsupported ipv scope: %d\n", scope); + continue; + } + + memcpy(offload->ipv6_addr[count], &ifaca6->aca_addr, + sizeof(ifaca6->aca_addr)); + offload->ipv6_type[count] = WMI_IPV6_AC_TYPE; + ath12k_dbg(ab, ATH12K_DBG_WOW, "mac count %d ipv6 ac %pI6 scope %d\n", + count, offload->ipv6_addr[count], + scope); + count++; + } + + rcu_read_unlock(); + +unlock: + read_unlock_bh(&idev->lock); + + in6_dev_put(idev); + + offload->ipv6_count = count; + ath12k_wow_generate_ns_mc_addr(ab, offload); +} + +static void ath12k_wow_prepare_arp_offload(struct ath12k_link_vif *arvif, + struct wmi_arp_ns_offload_arg *offload) +{ + struct ieee80211_vif *vif = arvif->ahvif->vif; + struct ieee80211_vif_cfg vif_cfg = vif->cfg; + struct ath12k_base *ab = arvif->ar->ab; + u32 ipv4_cnt; + + ath12k_dbg(ab, ATH12K_DBG_WOW, "wow prepare arp offload\n"); + + ipv4_cnt = min(vif_cfg.arp_addr_cnt, WMI_IPV4_MAX_COUNT); + memcpy(offload->ipv4_addr, vif_cfg.arp_addr_list, ipv4_cnt * sizeof(u32)); + offload->ipv4_count = ipv4_cnt; + + ath12k_dbg(ab, ATH12K_DBG_WOW, + "wow arp_addr_cnt %d vif->addr %pM, offload_addr %pI4\n", + vif_cfg.arp_addr_cnt, vif->addr, offload->ipv4_addr); +} + +static int ath12k_wow_arp_ns_offload(struct ath12k *ar, bool enable) +{ + struct wmi_arp_ns_offload_arg *offload; + struct ath12k_link_vif *arvif; + struct ath12k_vif *ahvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + offload = kmalloc(sizeof(*offload), GFP_KERNEL); + if (!offload) + return -ENOMEM; + + list_for_each_entry(arvif, &ar->arvifs, list) { + ahvif = arvif->ahvif; + + if (ahvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + memset(offload, 0, sizeof(*offload)); + + memcpy(offload->mac_addr, ahvif->vif->addr, ETH_ALEN); + ath12k_wow_prepare_ns_offload(arvif, offload); + ath12k_wow_prepare_arp_offload(arvif, offload); + + ret = ath12k_wmi_arp_ns_offload(ar, arvif, offload, enable); + if (ret) { + ath12k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n", + arvif->vdev_id, enable, ret); + return ret; + } + } + + kfree(offload); + + return 0; +} + +static int ath12k_gtk_rekey_offload(struct ath12k *ar, bool enable) +{ + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->ahvif->vdev_type != WMI_VDEV_TYPE_STA || + !arvif->is_up || + !arvif->rekey_data.enable_offload) + continue; + + /* get rekey info before disable rekey offload */ + if (!enable) { + ret = ath12k_wmi_gtk_rekey_getinfo(ar, arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + ret = ath12k_wmi_gtk_rekey_offload(ar, arvif, enable); + + if (ret) { + ath12k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n", + arvif->vdev_id, enable, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable) +{ + int ret; + + ret = ath12k_wow_arp_ns_offload(ar, enable); + if (ret) { + ath12k_warn(ar->ab, "failed to offload ARP and NS %d %d\n", + enable, ret); + return ret; + } + + ret = ath12k_gtk_rekey_offload(ar, enable); + if (ret) { + ath12k_warn(ar->ab, "failed to offload gtk rekey %d %d\n", + enable, ret); + return ret; + } + + return 0; +} + +static int ath12k_wow_set_keepalive(struct ath12k *ar, + enum wmi_sta_keepalive_method method, + u32 interval) +{ + struct ath12k_link_vif *arvif; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath12k_mac_vif_set_keepalive(arvif, method, interval); + if (ret) + return ret; + } + + return 0; +} + +int ath12k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + int ret; + + lockdep_assert_wiphy(hw->wiphy); + + ret = ath12k_wow_cleanup(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to clear wow wakeup events: %d\n", + ret); + goto exit; + } + + ret = ath12k_wow_set_wakeups(ar, wowlan); + if (ret) { + ath12k_warn(ar->ab, "failed to set wow wakeup events: %d\n", + ret); + goto cleanup; + } + + ret = ath12k_wow_protocol_offload(ar, true); + if (ret) { + ath12k_warn(ar->ab, "failed to set wow protocol offload events: %d\n", + ret); + goto cleanup; + } + + ret = ath12k_mac_wait_tx_complete(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); + goto cleanup; + } + + ret = ath12k_wow_set_hw_filter(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to set hw filter: %d\n", + ret); + goto cleanup; + } + + ret = ath12k_wow_set_keepalive(ar, + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME, + WMI_STA_KEEPALIVE_INTERVAL_DEFAULT); + if (ret) { + ath12k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret); + goto cleanup; + } + + ret = ath12k_wow_enable(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to start wow: %d\n", ret); + goto cleanup; + } + + ath12k_hif_irq_disable(ar->ab); + ath12k_hif_ce_irq_disable(ar->ab); + + ret = ath12k_hif_suspend(ar->ab); + if (ret) { + ath12k_warn(ar->ab, "failed to suspend hif: %d\n", ret); + goto wakeup; + } + + goto exit; + +wakeup: + ath12k_wow_wakeup(ar); + +cleanup: + ath12k_wow_cleanup(ar); + +exit: + return ret ? 1 : 0; +} + +void ath12k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + + lockdep_assert_wiphy(hw->wiphy); + + device_set_wakeup_enable(ar->ab->dev, enabled); +} + +int ath12k_wow_op_resume(struct ieee80211_hw *hw) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + int ret; + + lockdep_assert_wiphy(hw->wiphy); + + ret = ath12k_hif_resume(ar->ab); + if (ret) { + ath12k_warn(ar->ab, "failed to resume hif: %d\n", ret); + goto exit; + } + + ath12k_hif_ce_irq_enable(ar->ab); + ath12k_hif_irq_enable(ar->ab); + + ret = ath12k_wow_wakeup(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); + goto exit; + } + + ret = ath12k_wow_nlo_cleanup(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret); + goto exit; + } + + ret = ath12k_wow_clear_hw_filter(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to clear hw filter: %d\n", ret); + goto exit; + } + + ret = ath12k_wow_protocol_offload(ar, false); + if (ret) { + ath12k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n", + ret); + goto exit; + } + + ret = ath12k_wow_set_keepalive(ar, + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME, + WMI_STA_KEEPALIVE_INTERVAL_DISABLE); + if (ret) { + ath12k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret); + goto exit; + } + +exit: + if (ret) { + switch (ah->state) { + case ATH12K_HW_STATE_ON: + ah->state = ATH12K_HW_STATE_RESTARTING; + ret = 1; + break; + case ATH12K_HW_STATE_OFF: + case ATH12K_HW_STATE_RESTARTING: + case ATH12K_HW_STATE_RESTARTED: + case ATH12K_HW_STATE_WEDGED: + case ATH12K_HW_STATE_TM: + ath12k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", + ah->state); + ret = -EIO; + break; + } + } + + return ret; +} + +int ath12k_wow_init(struct ath12k *ar) +{ + if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map)) + return 0; + + ar->wow.wowlan_support = ath12k_wowlan_support; + + if (ar->ab->wow.wmi_conf_rx_decap_mode == ATH12K_HW_TXRX_NATIVE_WIFI) { + ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; + ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; + } + + if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { + ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; + ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; + } + + ar->wow.max_num_patterns = ATH12K_WOW_PATTERNS; + ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; + ar->ah->hw->wiphy->wowlan = &ar->wow.wowlan_support; + + device_set_wakeup_capable(ar->ab->dev, true); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/wow.h b/drivers/net/wireless/ath/ath12k/wow.h new file mode 100644 index 000000000000..af9be5fadcc3 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/wow.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef ATH12K_WOW_H +#define ATH12K_WOW_H + +#define ATH12K_WOW_RETRY_NUM 10 +#define ATH12K_WOW_RETRY_WAIT_MS 200 +#define ATH12K_WOW_PATTERNS 22 + +struct ath12k_wow { + u32 max_num_patterns; + struct completion wakeup_completed; + struct wiphy_wowlan_support wowlan_support; +}; + +struct ath12k_pkt_pattern { + u8 pattern[WOW_MAX_PATTERN_SIZE]; + u8 bytemask[WOW_MAX_PATTERN_SIZE]; + int pattern_len; + int pkt_offset; +}; + +struct rfc1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 eth_type; +} __packed; + +#ifdef CONFIG_PM + +int ath12k_wow_init(struct ath12k *ar); +int ath12k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int ath12k_wow_op_resume(struct ieee80211_hw *hw); +void ath12k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled); +int ath12k_wow_enable(struct ath12k *ar); +int ath12k_wow_wakeup(struct ath12k *ar); + +#else + +static inline int ath12k_wow_init(struct ath12k *ar) +{ + return 0; +} + +static inline int ath12k_wow_enable(struct ath12k *ar) +{ + return 0; +} + +static inline int ath12k_wow_wakeup(struct ath12k *ar) +{ + return 0; +} +#endif /* CONFIG_PM */ +#endif /* ATH12K_WOW_H */ diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index f27308ccb2f1..cb3e891ee1bd 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -218,10 +218,10 @@ static void ath_ahb_remove(struct platform_device *pdev) } static struct platform_driver ath_ahb_driver = { - .probe = ath_ahb_probe, - .remove_new = ath_ahb_remove, - .driver = { - .name = "ar231x-wmac", + .probe = ath_ahb_probe, + .remove = ath_ahb_remove, + .driver = { + .name = "ar231x-wmac", }, }; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 9f534ed2fbb3..4d88b02ffa79 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -59,7 +59,7 @@ #include <net/cfg80211.h> #include <net/ieee80211_radiotap.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <net/mac80211.h> #include "base.h" @@ -2847,7 +2847,7 @@ static void ath5k_stop_tasklets(struct ath5k_hw *ah) * if another thread does a system call and the thread doing the * stop is preempted). */ -void ath5k_stop(struct ieee80211_hw *hw) +void ath5k_stop(struct ieee80211_hw *hw, bool suspend) { struct ath5k_hw *ah = hw->priv; int ret; diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index 97469d0fbad7..594e5b945cb7 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -92,7 +92,7 @@ void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif); bool ath5k_any_vif_assoc(struct ath5k_hw *ah); int ath5k_start(struct ieee80211_hw *hw); -void ath5k_stop(struct ieee80211_hw *hw); +void ath5k_stop(struct ieee80211_hw *hw, bool suspend); void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf); int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index eea4bda77608..d81b2ad0b095 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -44,7 +44,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <net/mac80211.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "ath5k.h" #include "base.h" diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c index b51fce5ae260..f5ca2fe0d074 100644 --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -46,6 +46,8 @@ static const struct pci_device_id ath5k_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */ { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */ { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */ + { PCI_VDEVICE(ATHEROS, 0xff16) }, /* Gigaset SX76[23] AR241[34]A */ + { PCI_VDEVICE(ATHEROS, 0xff1a) }, /* Arcadyan ARV45XX AR2417 */ { PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */ { 0 } }; diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 3f4ce4e9c532..90e0859a8e50 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -24,7 +24,7 @@ * Protocol Control Unit Functions * \*********************************/ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "ath5k.h" #include "reg.h" diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 7ee4e1616f45..4825f9cb9cb8 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -27,7 +27,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/sort.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "ath5k.h" #include "reg.h" diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 9fdb5283b39c..c67f163c0858 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -25,7 +25,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/pci.h> /* To determine if a card is pci-e */ #include <linux/log2.h> diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c index af98e871199d..5a9e93fd1ef4 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.c +++ b/drivers/net/wireless/ath/ath6kl/bmi.c @@ -87,7 +87,9 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, * We need to do some backwards compatibility to make this work. */ if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { - WARN_ON(1); + ath6kl_err("mismatched byte count %d vs. expected %zd\n", + le32_to_cpu(targ_info->byte_count), + sizeof(*targ_info)); return -EINVAL; } diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 61b2e3f15f0e..8c2e8081112e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -149,7 +149,7 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags)) return false; - del_timer_sync(&vif->sched_scan_timer); + timer_delete_sync(&vif->sched_scan_timer); if (ar->state == ATH6KL_STATE_RECOVERY) return true; @@ -1200,7 +1200,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (((vif->auth_mode == WPA_PSK_AUTH) || (vif->auth_mode == WPA2_PSK_AUTH)) && (key_usage & GROUP_USAGE)) - del_timer(&vif->disconnect_timer); + timer_delete(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n", @@ -1441,6 +1441,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, struct wireless_dev *wdev, + unsigned int link_id, int *dbm) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); @@ -3611,7 +3612,7 @@ void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready) discon_issued = test_bit(CONNECTED, &vif->flags) || test_bit(CONNECT_PEND, &vif->flags); ath6kl_disconnect(vif); - del_timer(&vif->disconnect_timer); + timer_delete(&vif->disconnect_timer); if (discon_issued) ath6kl_disconnect_event(vif, DISCONNECT_CMD, diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index 1963d3145481..f8a94d764be6 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -21,7 +21,7 @@ #include "hif-ops.h" #include "trace.h" -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) @@ -364,8 +364,7 @@ static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags, packet->buf -= HTC_HDR_LENGTH; hdr = (struct htc_frame_hdr *)packet->buf; - /* Endianess? */ - put_unaligned((u16)packet->act_len, &hdr->payld_len); + put_unaligned_le16(packet->act_len, &hdr->payld_len); hdr->flags = flags; hdr->eid = packet->endpoint; hdr->ctrl[0] = ctrl0; diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index 9b88d96bfe96..2f2edfe43761 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -237,8 +237,7 @@ static int htc_issue_packets(struct htc_target *target, packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; - /* Endianess? */ - put_unaligned((u16) payload_len, &htc_hdr->payld_len); + put_unaligned_le16(payload_len, &htc_hdr->payld_len); htc_hdr->flags = packet->info.tx.flags; htc_hdr->eid = (u8) packet->endpoint; htc_hdr->ctrl[0] = 0; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 15f455adb860..9b100ee2ebc3 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1915,7 +1915,7 @@ void ath6kl_stop_txrx(struct ath6kl *ar) clear_bit(WMI_READY, &ar->flag); if (ar->fw_recovery.enable) - del_timer_sync(&ar->fw_recovery.hb_timer); + timer_delete_sync(&ar->fw_recovery.hb_timer); /* * After wmi_shudown all WMI events will be dropped. We diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 8f9fe23e9755..d62b96459751 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -503,7 +503,7 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, void disconnect_timer_handler(struct timer_list *t) { - struct ath6kl_vif *vif = from_timer(vif, t, disconnect_timer); + struct ath6kl_vif *vif = timer_container_of(vif, t, disconnect_timer); ath6kl_init_profile_info(vif); ath6kl_disconnect(vif); @@ -1027,7 +1027,7 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, aggr_reset_state(vif->aggr_cntxt->aggr_conn); - del_timer(&vif->disconnect_timer); + timer_delete(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason); diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index c09e40c9010f..43186c193df1 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -25,7 +25,7 @@ static void ath6kl_recovery_work(struct work_struct *work) ar->state = ATH6KL_STATE_RECOVERY; - del_timer_sync(&ar->fw_recovery.hb_timer); + timer_delete_sync(&ar->fw_recovery.hb_timer); ath6kl_init_hw_restart(ar); @@ -62,7 +62,7 @@ void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie) static void ath6kl_recovery_hb_timer(struct timer_list *t) { - struct ath6kl *ar = from_timer(ar, t, fw_recovery.hb_timer); + struct ath6kl *ar = timer_container_of(ar, t, fw_recovery.hb_timer); int err; if (test_bit(RECOVERY_CLEANUP, &ar->flag) || @@ -119,7 +119,7 @@ void ath6kl_recovery_cleanup(struct ath6kl *ar) set_bit(RECOVERY_CLEANUP, &ar->flag); - del_timer_sync(&ar->fw_recovery.hb_timer); + timer_delete_sync(&ar->fw_recovery.hb_timer); cancel_work_sync(&ar->fw_recovery.recovery_work); } diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 8a43c48ec1cf..9ab091044706 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -1427,25 +1427,7 @@ static struct sdio_driver ath6kl_sdio_driver = { .remove = ath6kl_sdio_remove, .drv.pm = ATH6KL_SDIO_PM_OPS, }; - -static int __init ath6kl_sdio_init(void) -{ - int ret; - - ret = sdio_register_driver(&ath6kl_sdio_driver); - if (ret) - ath6kl_err("sdio driver registration failed: %d\n", ret); - - return ret; -} - -static void __exit ath6kl_sdio_exit(void) -{ - sdio_unregister_driver(&ath6kl_sdio_driver); -} - -module_init(ath6kl_sdio_init); -module_exit(ath6kl_sdio_exit); +module_sdio_driver(ath6kl_sdio_driver); MODULE_AUTHOR("Atheros Communications, Inc."); MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); diff --git a/drivers/net/wireless/ath/ath6kl/trace.h b/drivers/net/wireless/ath/ath6kl/trace.h index 231a94769ddb..8577aa459c58 100644 --- a/drivers/net/wireless/ath/ath6kl/trace.h +++ b/drivers/net/wireless/ath/ath6kl/trace.h @@ -304,8 +304,8 @@ TRACE_EVENT(ath6kl_log_dbg_dump, ), TP_fast_assign( - __assign_str(msg, msg); - __assign_str(prefix, prefix); + __assign_str(msg); + __assign_str(prefix); __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); ), diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 80e66acc5cf6..c3b06b515c4f 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1623,7 +1623,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) static void aggr_timeout(struct timer_list *t) { u8 i, j; - struct aggr_info_conn *aggr_conn = from_timer(aggr_conn, t, timer); + struct aggr_info_conn *aggr_conn = timer_container_of(aggr_conn, t, + timer); struct rxtid *rxtid; struct rxtid_stats *stats; @@ -1827,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn) return; if (aggr_conn->timer_scheduled) { - del_timer(&aggr_conn->timer); + timer_delete(&aggr_conn->timer); aggr_conn->timer_scheduled = false; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 3787b9fb0075..84317afe4651 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1078,7 +1078,7 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len, void ath6kl_wmi_sscan_timer(struct timer_list *t) { - struct ath6kl_vif *vif = from_timer(vif, t, sched_scan_timer); + struct ath6kl_vif *vif = timer_container_of(vif, t, sched_scan_timer); cfg80211_sched_scan_results(vif->ar->wiphy, 0); } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index b4fcfb72991c..68384159870b 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1249,7 +1249,7 @@ struct wmi_rssi_threshold_params_cmd { /* highest of upper */ a_sle16 thresh_above6_val; - /* lowest of bellow */ + /* lowest of below */ a_sle16 thresh_below1_val; a_sle16 thresh_below2_val; @@ -1257,7 +1257,7 @@ struct wmi_rssi_threshold_params_cmd { a_sle16 thresh_below4_val; a_sle16 thresh_below5_val; - /* highest of bellow */ + /* highest of below */ a_sle16 thresh_below6_val; /* "alpha" */ @@ -1287,13 +1287,13 @@ struct wmi_snr_threshold_params_cmd { /* highest of upper */ u8 thresh_above4_val; - /* lowest of bellow */ + /* lowest of below */ u8 thresh_below1_val; u8 thresh_below2_val; u8 thresh_below3_val; - /* highest of bellow */ + /* highest of below */ u8 thresh_below4_val; u8 reserved[3]; diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 1a6697b6e3b4..49b7ab26c477 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -74,7 +74,6 @@ static int ath_ahb_probe(struct platform_device *pdev) void __iomem *mem; struct ath_softc *sc; struct ieee80211_hw *hw; - struct resource *res; const struct platform_device_id *id = platform_get_device_id(pdev); int irq; int ret = 0; @@ -86,16 +85,10 @@ static int ath_ahb_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no memory resource found\n"); - return -ENXIO; - } - - mem = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (mem == NULL) { + mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem)) { dev_err(&pdev->dev, "ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(mem); } irq = platform_get_irq(pdev, 0); @@ -158,12 +151,12 @@ static void ath_ahb_remove(struct platform_device *pdev) } static struct platform_driver ath_ahb_driver = { - .probe = ath_ahb_probe, - .remove_new = ath_ahb_remove, - .driver = { - .name = "ath9k", + .probe = ath_ahb_probe, + .remove = ath_ahb_remove, + .driver = { + .name = "ath9k", }, - .id_table = ath9k_platform_id_table, + .id_table = ath9k_platform_id_table, }; MODULE_DEVICE_TABLE(platform, ath9k_platform_id_table); diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index acc84e6711b0..e5e274bc9e68 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -193,7 +193,7 @@ static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb, struct ath_hw_antcomb_conf *conf) { - /* set alt to the conf with maximun ratio */ + /* set alt to the conf with maximum ratio */ if (antcomb->first_ratio && antcomb->second_ratio) { if (antcomb->rssi_second > antcomb->rssi_third) { /* first alt*/ diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index d08ea0b28530..b26224480041 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -395,7 +395,7 @@ static void ar9002_hw_init_hang_checks(struct ath_hw *ah) ah->config.hw_hang_checks |= HW_MAC_HANG; } -/* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */ +/* Sets up the AR5008/AR9001/AR9002 hardware family callbacks */ int ar9002_hw_attach_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.c b/drivers/net/wireless/ath/ath9k/ar9003_aic.c index d0f1e8bcc846..45a7ca660f47 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_aic.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.c @@ -409,13 +409,11 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) sram.valid = true; sram.rot_dir_att_db = - min(max(rot_dir_path_att_db, - (int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB), - ATH_AIC_MAX_ROT_DIR_ATT_DB); + clamp(rot_dir_path_att_db, (int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB, + ATH_AIC_MAX_ROT_DIR_ATT_DB); sram.rot_quad_att_db = - min(max(rot_quad_path_att_db, - (int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB), - ATH_AIC_MAX_ROT_QUAD_ATT_DB); + clamp(rot_quad_path_att_db, (int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB, + ATH_AIC_MAX_ROT_QUAD_ATT_DB); aic->aic_sram[i] = (SM(sram.vga_dir_sign, AR_PHY_AIC_SRAM_VGA_DIR_SIGN) | diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 944f46cdf34c..73c38a6b4880 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/kernel.h> #include "hw.h" #include "ar9003_phy.h" diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index e9bd13eeee92..6595eca74997 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -1170,7 +1170,7 @@ exit: return false; } -/* Sets up the AR9003 hardware familiy callbacks */ +/* Sets up the AR9003 hardware family callbacks */ void ar9003_hw_attach_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 2b9c07961cd7..3f0543e55d9b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -637,7 +637,7 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, * same time. Since BT's calibration doesn't happen * that often, we'll let BT completes calibration then * we continue to wait for cal_grant from BT. - * Orginal: Wait BT_CAL_GRANT. + * Original: Wait BT_CAL_GRANT. * New: Receive BT_CAL_REQ -> send WLAN_CAL_GRANT->wait * BT_CAL_DONE -> Wait BT_CAL_GRANT. */ @@ -747,7 +747,7 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, * BT is sleeping. Check if BT wakes up during * WLAN calibration. If BT wakes up during * WLAN calibration, need to go through all - * message exchanges again and recal. + * message exchanges again and recalibrate. */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index ad72a30b67c3..e13873fb8e2f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -246,7 +246,7 @@ /* - * MRC Feild Definitions + * MRC Field Definitions */ #define AR_PHY_SGI_DSC_MAN 0x0007FFF0 #define AR_PHY_SGI_DSC_MAN_S 4 diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 668fc07b3073..6e38aa7351e3 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -39,6 +39,7 @@ extern int ath9k_modparam_nohwcrypt; extern int ath9k_led_blink; extern bool is_ath9k_unloaded; extern int ath9k_use_chanctx; +extern int ath9k_use_msi; /*************************/ /* Descriptor Management */ @@ -273,7 +274,6 @@ struct ath_node { struct ath_tx_control { struct ath_txq *txq; - struct ath_node *an; struct ieee80211_sta *sta; u8 paprd; }; @@ -337,7 +337,7 @@ struct ath_chanctx { struct ath_beacon_config beacon; struct ath9k_hw_cal_data caldata; - struct timespec64 tsf_ts; + ktime_t tsf_ts; u64 tsf_val; u32 last_beacon; @@ -591,8 +591,8 @@ void ath_txq_schedule_all(struct ath_softc *sc); int ath_tx_init(struct ath_softc *sc, int nbufs); int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_tx_queue_info *q); -u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, - int width, int half_gi, bool shortPreamble); +u32 ath_pkt_duration(u8 rix, int pktlen, int width, + int half_gi, bool shortPreamble); void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); void ath_assign_seq(struct ath_common *common, struct sk_buff *skb); int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, @@ -1010,13 +1010,15 @@ struct ath_softc { struct ath_offchannel offchannel; struct ath_chanctx *next_chan; struct completion go_beacon; - struct timespec64 last_event_time; + ktime_t last_event_time; #endif unsigned long driver_data; u8 gtt_cnt; u32 intrstatus; + unsigned long rx_active_check_time; + u32 rx_active_count; u16 ps_flags; /* PS_* */ bool ps_enabled; bool ps_idle; diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c index a5eb43f30320..fe1013a3a588 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c @@ -18,7 +18,6 @@ #include <linux/pci.h> #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/ath9k_platform.h> #include <linux/nvmem-consumer.h> #include <linux/workqueue.h> @@ -65,7 +64,7 @@ static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data, dev_info(&pdev->dev, "fixup device configuration\n"); - mem = pcim_iomap(pdev, 0, 0); + mem = pci_iomap(pdev, 0, 0); if (!mem) { dev_err(&pdev->dev, "ioremap error\n"); return -EINVAL; @@ -103,7 +102,7 @@ static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data, pci_write_config_word(pdev, PCI_COMMAND, cmd); pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0); - pcim_iounmap(pdev, mem); + pci_iounmap(pdev, mem); pci_disable_device(pdev); @@ -200,11 +199,9 @@ static int owl_probe(struct pci_dev *pdev, const char *eeprom_name; int err = 0; - if (pcim_enable_device(pdev)) + if (pci_enable_device(pdev)) return -EIO; - pcim_pin_device(pdev); - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index b399a7926ef5..4a27e3753c03 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -293,7 +293,7 @@ void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc) /* Modify TSF as required and update the HW. */ avp->chanctx->tsf_val += tsfadjust; if (sc->cur_chan == avp->chanctx) { - offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL); + offset = ath9k_hw_get_tsf_offset(avp->chanctx->tsf_ts, 0); ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset); } diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 9b393a8f7c3a..ad3a3fda1b9c 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -16,7 +16,6 @@ #include <linux/export.h> #include <linux/types.h> -#include <linux/ath9k_platform.h> #include "hw.h" enum ath_bt_mode { @@ -115,23 +114,14 @@ static void ath9k_hw_btcoex_pin_init(struct ath_hw *ah, u8 wlanactive_gpio, u8 btactive_gpio, u8 btpriority_gpio) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - struct ath9k_platform_data *pdata = ah->dev->platform_data; if (btcoex_hw->scheme != ATH_BTCOEX_CFG_2WIRE && btcoex_hw->scheme != ATH_BTCOEX_CFG_3WIRE) return; - /* bt priority GPIO will be ignored by 2 wire scheme */ - if (pdata && (pdata->bt_active_pin || pdata->bt_priority_pin || - pdata->wlan_active_pin)) { - btcoex_hw->btactive_gpio = pdata->bt_active_pin; - btcoex_hw->wlanactive_gpio = pdata->wlan_active_pin; - btcoex_hw->btpriority_gpio = pdata->bt_priority_pin; - } else { - btcoex_hw->btactive_gpio = btactive_gpio; - btcoex_hw->wlanactive_gpio = wlanactive_gpio; - btcoex_hw->btpriority_gpio = btpriority_gpio; - } + btcoex_hw->btactive_gpio = btactive_gpio; + btcoex_hw->wlanactive_gpio = wlanactive_gpio; + btcoex_hw->btpriority_gpio = btpriority_gpio; } void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index fb270df75eb2..b4ab85bd7895 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -16,32 +16,25 @@ #include "hw.h" #include "hw-ops.h" +#include <linux/sort.h> #include <linux/export.h> /* Common calibration code */ +static int rcmp_i16(const void *x, const void *y) +{ + /* Sort in reverse order. */ + return *(int16_t *)y - *(int16_t *)x; +} static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { - int16_t nfval; - int16_t sort[ATH9K_NF_CAL_HIST_MAX]; - int i, j; - - for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) - sort[i] = nfCalBuffer[i]; - - for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { - for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { - if (sort[j] > sort[j - 1]) { - nfval = sort[j]; - sort[j] = sort[j - 1]; - sort[j - 1] = nfval; - } - } - } - nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; + int16_t nfcal[ATH9K_NF_CAL_HIST_MAX]; + + memcpy(nfcal, nfCalBuffer, sizeof(nfcal)); + sort(nfcal, ATH9K_NF_CAL_HIST_MAX, sizeof(int16_t), rcmp_i16, NULL); - return nfval; + return nfcal[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; } static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 571062f2e82a..121e51ce1bc0 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -17,7 +17,7 @@ #include "ath9k.h" /* Set/change channels. If the channel is really being changed, it's done - * by reseting the chip. To accomplish this we must first cleanup any pending + * by resetting the chip. To accomplish this we must first cleanup any pending * DMA, then restart stuff. */ static int ath_set_channel(struct ath_softc *sc) @@ -232,16 +232,11 @@ static const char *chanctx_state_string(enum ath_chanctx_state state) static u32 chanctx_event_delta(struct ath_softc *sc) { - u64 ms; - struct timespec64 ts, *old; + ktime_t ts = ktime_get_raw(); + s64 ms = ktime_ms_delta(ts, sc->last_event_time); - ktime_get_raw_ts64(&ts); - old = &sc->last_event_time; - ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; - ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000; sc->last_event_time = ts; - - return (u32)ms; + return ms; } void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) @@ -334,8 +329,8 @@ ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx) static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) { struct ath_chanctx *prev, *cur; - struct timespec64 ts; u32 cur_tsf, prev_tsf, beacon_int; + ktime_t ts; s32 offset; beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); @@ -346,12 +341,12 @@ static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) if (!prev->switch_after_beacon) return; - ktime_get_raw_ts64(&ts); + ts = ktime_get_raw(); cur_tsf = (u32) cur->tsf_val + - ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); + ath9k_hw_get_tsf_offset(cur->tsf_ts, ts); prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf; - prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts); + prev_tsf -= ath9k_hw_get_tsf_offset(prev->tsf_ts, ts); /* Adjust the TSF time of the AP chanctx to keep its beacons * at half beacon interval offset relative to the STA chanctx. @@ -691,7 +686,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, */ tsf_time = sc->sched.switch_start_time; tsf_time -= (u32) sc->cur_chan->tsf_val + - ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); + ath9k_hw_get_tsf_offset(sc->cur_chan->tsf_ts, 0); tsf_time += ath9k_hw_gettsf32(ah); sc->sched.beacon_adjust = false; @@ -1045,7 +1040,7 @@ static void ath_scan_channel_start(struct ath_softc *sc) static void ath_chanctx_timer(struct timer_list *t) { - struct ath_softc *sc = from_timer(sc, t, sched.timer); + struct ath_softc *sc = timer_container_of(sc, t, sched.timer); struct ath_common *common = ath9k_hw_common(sc->sc_ah); ath_dbg(common, CHAN_CTX, @@ -1056,7 +1051,7 @@ static void ath_chanctx_timer(struct timer_list *t) static void ath_offchannel_timer(struct timer_list *t) { - struct ath_softc *sc = from_timer(sc, t, offchannel.timer); + struct ath_softc *sc = timer_container_of(sc, t, offchannel.timer); struct ath_chanctx *ctx; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -1230,10 +1225,10 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *old_ctx; - struct timespec64 ts; bool measure_time = false; bool send_ps = false; bool queues_stopped = false; + ktime_t ts; spin_lock_bh(&sc->chan_lock); if (!sc->next_chan) { @@ -1260,7 +1255,7 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force) spin_unlock_bh(&sc->chan_lock); if (sc->next_chan == &sc->offchannel.chan) { - ktime_get_raw_ts64(&ts); + ts = ktime_get_raw(); measure_time = true; } @@ -1277,7 +1272,7 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force) spin_lock_bh(&sc->chan_lock); if (sc->cur_chan != &sc->offchannel.chan) { - ktime_get_raw_ts64(&sc->cur_chan->tsf_ts); + sc->cur_chan->tsf_ts = ktime_get_raw(); sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah); } } @@ -1303,7 +1298,7 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force) ath_set_channel(sc); if (measure_time) sc->sched.channel_switch_time = - ath9k_hw_get_tsf_offset(&ts, NULL); + ath9k_hw_get_tsf_offset(ts, 0); /* * A reset will ensure that all queues are woken up, * so there is no need to awaken them again. @@ -1561,7 +1556,7 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; - del_timer_sync(&sc->sched.timer); + timer_delete_sync(&sc->sched.timer); ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 4b27445a5fb8..300d178830ad 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -628,12 +628,12 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h else RX_STAT_INC(sc, rx_spectral_sample_err); - memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); - /* Mix the received bins to the /dev/random * pool */ add_device_randomness(sample_buf, num_bins); + + memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); } /* Process a normal frame */ @@ -734,7 +734,7 @@ void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, ATH9K_RX_FILTER_PHYRADAR | ATH9K_RX_FILTER_PHYERR); - /* TODO: usually this should not be neccesary, but for some reason + /* TODO: usually this should not be necessary, but for some reason * (or in some mode?) the trigger must be called after the * configuration, otherwise the register will have its values reset * (on my ar9220 to value 0x01002310) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index d84e3ee7b5d9..74a0134075cf 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -17,7 +17,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/export.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "ath9k.h" @@ -750,6 +750,7 @@ static int read_file_reset(struct seq_file *file, void *data) [RESET_TYPE_CALIBRATION] = "Calibration error", [RESET_TX_DMA_ERROR] = "Tx DMA stop error", [RESET_RX_DMA_ERROR] = "Rx DMA stop error", + [RESET_TYPE_RX_INACTIVE] = "Rx path inactive", }; int i; @@ -1325,11 +1326,11 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; int i = 0; - data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_pkts_all + + data[i++] = ((u64)sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_pkts_all + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_pkts_all + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_pkts_all + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_pkts_all); - data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_bytes_all + + data[i++] = ((u64)sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_bytes_all + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_bytes_all + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_bytes_all + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_bytes_all); @@ -1380,8 +1381,6 @@ int ath9k_init_debug(struct ath_hw *ah) sc->debug.debugfs_phy = debugfs_create_dir("ath9k", sc->hw->wiphy->debugfsdir); - if (IS_ERR(sc->debug.debugfs_phy)) - return -ENOMEM; #ifdef CONFIG_ATH_DEBUG debugfs_create_file("debug", 0600, sc->debug.debugfs_phy, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 389459c04d14..cb3e75969875 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -53,6 +53,7 @@ enum ath_reset_type { RESET_TYPE_CALIBRATION, RESET_TX_DMA_ERROR, RESET_RX_DMA_ERROR, + RESET_TYPE_RX_INACTIVE, __RESET_TYPE_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c index 11349218bc21..2fb73a5e1d51 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.c +++ b/drivers/net/wireless/ath/ath9k/dfs.c @@ -79,7 +79,7 @@ static int ath9k_get_max_index_ht40(struct ath9k_dfs_fft_40 *fft, const int DFS_UPPER_BIN_OFFSET = 64; /* if detected radar on both channels, select the significant one */ if (is_ctl && is_ext) { - /* first check wether channels have 'strong' bins */ + /* first check whether channels have 'strong' bins */ is_ctl = fft_bitmap_weight(fft->lower_bins) != 0; is_ext = fft_bitmap_weight(fft->upper_bins) != 0; @@ -280,7 +280,7 @@ ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe) if (!pd->add_pulse(pd, pe, NULL)) return; DFS_STAT_INC(sc, radar_detected); - ieee80211_radar_detected(sc->hw); + ieee80211_radar_detected(sc->hw, NULL); } /* diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 8e18e9b4ef48..426caa057396 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -116,7 +116,7 @@ static ssize_t write_file_simulate_radar(struct file *file, { struct ath_softc *sc = file->private_data; - ieee80211_radar_detected(sc->hw); + ieee80211_radar_detected(sc->hw, NULL); return count; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index efb7889142d4..df58dc02e104 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -15,7 +15,6 @@ */ #include "hw.h" -#include <linux/ath9k_platform.h> void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val) { @@ -119,14 +118,6 @@ static bool ath9k_hw_nvram_read_array(u16 *blob, size_t blob_size, return true; } -static bool ath9k_hw_nvram_read_pdata(struct ath9k_platform_data *pdata, - off_t offset, u16 *data) -{ - return ath9k_hw_nvram_read_array(pdata->eeprom_data, - ARRAY_SIZE(pdata->eeprom_data), - offset, data); -} - static bool ath9k_hw_nvram_read_firmware(const struct firmware *eeprom_blob, off_t offset, u16 *data) { @@ -146,15 +137,12 @@ static bool ath9k_hw_nvram_read_nvmem(struct ath_hw *ah, off_t offset, bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) { struct ath_common *common = ath9k_hw_common(ah); - struct ath9k_platform_data *pdata = ah->dev->platform_data; bool ret; if (ah->nvmem_blob) ret = ath9k_hw_nvram_read_nvmem(ah, off, data); else if (ah->eeprom_blob) ret = ath9k_hw_nvram_read_firmware(ah->eeprom_blob, off, data); - else if (pdata && !pdata->use_eeprom) - ret = ath9k_hw_nvram_read_pdata(pdata, off, data); else ret = common->bus_ops->eeprom_read(common, off, data); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index e8c2cc03be0c..3e16cfe059f3 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hw.h" #include "ar9002_phy.h" @@ -76,7 +76,7 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) static u32 ath9k_dump_4k_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_4k_header *modal_hdr) { - PR_EEP("Chain0 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[0])); + PR_EEP("Chain0 Ant. Control", le32_to_cpu(modal_hdr->antCtrlChain[0])); PR_EEP("Ant. Common Control", le32_to_cpu(modal_hdr->antCtrlCommon)); PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]); PR_EEP("Switch Settle", modal_hdr->switchSettling); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index fd5312c2a7e3..c139ac49ccf6 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hw.h" #include "ar9002_phy.h" @@ -79,8 +79,8 @@ static bool ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) static u32 ar9287_dump_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_ar9287_header *modal_hdr) { - PR_EEP("Chain0 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[0])); - PR_EEP("Chain1 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[1])); + PR_EEP("Chain0 Ant. Control", le32_to_cpu(modal_hdr->antCtrlChain[0])); + PR_EEP("Chain1 Ant. Control", le32_to_cpu(modal_hdr->antCtrlChain[1])); PR_EEP("Ant. Common Control", le32_to_cpu(modal_hdr->antCtrlCommon)); PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]); PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 7685f8ab371e..5ba467cb7425 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hw.h" #include "ar9002_phy.h" @@ -135,9 +135,9 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_header *modal_hdr) { - PR_EEP("Chain0 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[0])); - PR_EEP("Chain1 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[1])); - PR_EEP("Chain2 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[2])); + PR_EEP("Chain0 Ant. Control", le32_to_cpu(modal_hdr->antCtrlChain[0])); + PR_EEP("Chain1 Ant. Control", le32_to_cpu(modal_hdr->antCtrlChain[1])); + PR_EEP("Chain2 Ant. Control", le32_to_cpu(modal_hdr->antCtrlChain[2])); PR_EEP("Ant. Common Control", le32_to_cpu(modal_hdr->antCtrlCommon)); PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]); PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]); diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index b457e52dd365..2dbc7efdd637 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -193,7 +193,7 @@ static void ath_mci_ftp_adjust(struct ath_softc *sc) */ static void ath_btcoex_period_timer(struct timer_list *t) { - struct ath_softc *sc = from_timer(sc, t, btcoex.period_timer); + struct ath_softc *sc = timer_container_of(sc, t, btcoex.period_timer); struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; enum ath_stomp_type stomp_type; @@ -254,7 +254,8 @@ skip_hw_wakeup: */ static void ath_btcoex_no_stomp_timer(struct timer_list *t) { - struct ath_softc *sc = from_timer(sc, t, btcoex.no_stomp_timer); + struct ath_softc *sc = timer_container_of(sc, t, + btcoex.no_stomp_timer); struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; @@ -305,7 +306,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ - del_timer_sync(&btcoex->no_stomp_timer); + timer_delete_sync(&btcoex->no_stomp_timer); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; @@ -329,15 +330,15 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n"); - del_timer_sync(&btcoex->period_timer); - del_timer_sync(&btcoex->no_stomp_timer); + timer_delete_sync(&btcoex->period_timer); + timer_delete_sync(&btcoex->no_stomp_timer); } void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - del_timer_sync(&btcoex->no_stomp_timer); + timer_delete_sync(&btcoex->no_stomp_timer); } u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 0c7841f95228..fe9abe8cd268 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "htc.h" MODULE_FIRMWARE(HTC_7010_MODULE_FW); @@ -716,8 +716,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb) } resubmit: - skb_reset_tail_pointer(skb); - skb_trim(skb, 0); + __skb_set_length(skb, 0); usb_anchor_urb(urb, &hif_dev->rx_submitted); ret = usb_submit_urb(urb, GFP_ATOMIC); @@ -754,8 +753,7 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) case -ESHUTDOWN: goto free_skb; default: - skb_reset_tail_pointer(skb); - skb_trim(skb, 0); + __skb_set_length(skb, 0); goto resubmit; } @@ -1200,7 +1198,7 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev, filename = FIRMWARE_AR9271; /* expected fw locations: - * - htc_9271.fw (stable version 1.3, depricated) + * - htc_9271.fw (stable version 1.3, deprecated) */ snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name), "%s", filename); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 547634f82183..81fa7cbad892 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, struct ath_common *common = ath9k_hw_common(priv->ah); int slot; + if (!priv->cur_beacon_conf.enable_beacon) + return; + if (swba->beacon_pending != 0) { priv->beacon.bmisscnt++; if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) { diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index f7c6d9bc9311..9437d69877cc 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -486,8 +486,6 @@ int ath9k_htc_init_debug(struct ath_hw *ah) priv->debug.debugfs_phy = debugfs_create_dir(KBUILD_MODNAME, priv->hw->wiphy->debugfsdir); - if (IS_ERR(priv->debug.debugfs_phy)) - return -ENOMEM; ath9k_cmn_spectral_init_debug(&priv->spec_priv, priv->debug.debugfs_phy); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index b389e19381c4..19600018e562 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -198,7 +198,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) ath9k_htc_stop_ani(priv); ieee80211_stop_queues(priv->hw); - del_timer_sync(&priv->tx.cleanup_timer); + timer_delete_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); @@ -260,7 +260,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, ath9k_htc_ps_wakeup(priv); ath9k_htc_stop_ani(priv); - del_timer_sync(&priv->tx.cleanup_timer); + timer_delete_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); @@ -973,7 +973,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) return ret; } -static void ath9k_htc_stop(struct ieee80211_hw *hw) +static void ath9k_htc_stop(struct ieee80211_hw *hw, bool suspend) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; @@ -997,7 +997,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) tasklet_kill(&priv->rx_tasklet); - del_timer_sync(&priv->tx.cleanup_timer); + timer_delete_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); ath9k_wmi_event_drain(priv); @@ -1357,8 +1357,10 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, static void ath9k_htc_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u32 changed) + struct ieee80211_link_sta *link_sta, + u32 changed) { + struct ieee80211_sta *sta = link_sta->sta; struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; if (!(changed & IEEE80211_RC_SUPP_RATES_CHANGED)) @@ -1883,7 +1885,7 @@ struct ieee80211_ops ath9k_htc_ops = { .sta_add = ath9k_htc_sta_add, .sta_remove = ath9k_htc_sta_remove, .conf_tx = ath9k_htc_conf_tx, - .sta_rc_update = ath9k_htc_sta_rc_update, + .link_sta_rc_update = ath9k_htc_sta_rc_update, .bss_info_changed = ath9k_htc_bss_info_changed, .set_key = ath9k_htc_set_key, .get_tsf = ath9k_htc_get_tsf, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index ce9c04e418b8..ee5945cfc10e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -760,7 +760,8 @@ static void ath9k_htc_tx_cleanup_queue(struct ath9k_htc_priv *priv, void ath9k_htc_tx_cleanup_timer(struct timer_list *t) { - struct ath9k_htc_priv *priv = from_timer(priv, t, tx.cleanup_timer); + struct ath9k_htc_priv *priv = timer_container_of(priv, t, + tx.cleanup_timer); struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_tx_event *event, *tmp; struct sk_buff *skb; diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index eb631fd3336d..b5257b2b4aa5 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -294,6 +294,9 @@ int htc_connect_service(struct htc_target *target, return -ETIMEDOUT; } + if (target->conn_rsp_epid < 0 || target->conn_rsp_epid >= ENDPOINT_MAX) + return -EINVAL; + *conn_rsp_epid = target->conn_rsp_epid; return 0; err: diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 5982e0db45f9..f9a774bd0e13 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -21,7 +21,7 @@ #include <linux/bitops.h> #include <linux/etherdevice.h> #include <linux/gpio.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hw.h" #include "hw-ops.h" @@ -490,7 +490,7 @@ static void ath9k_hw_init_macaddr(struct ath_hw *ah) u16 eeval; static const u32 EEP_MAC[] = { EEP_MAC_LSW, EEP_MAC_MID, EEP_MAC_MSW }; - /* MAC address may already be loaded via ath9k_platform_data */ + /* MAC address may already be loaded via NVMEM */ if (is_valid_ether_addr(common->macaddr)) return; @@ -1847,20 +1847,11 @@ fail: return -EINVAL; } -u32 ath9k_hw_get_tsf_offset(struct timespec64 *last, struct timespec64 *cur) +u32 ath9k_hw_get_tsf_offset(ktime_t last, ktime_t cur) { - struct timespec64 ts; - s64 usec; - - if (!cur) { - ktime_get_raw_ts64(&ts); - cur = &ts; - } - - usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000; - usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000; - - return (u32) usec; + if (cur == 0) + cur = ktime_get_raw(); + return ktime_us_delta(cur, last); } EXPORT_SYMBOL(ath9k_hw_get_tsf_offset); @@ -1871,7 +1862,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, u32 saveLedState; u32 saveDefAntenna; u32 macStaId1; - struct timespec64 tsf_ts; + ktime_t tsf_ts; u32 tsf_offset; u64 tsf = 0; int r; @@ -1917,7 +1908,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; /* Save TSF before chip reset, a cold reset clears it */ - ktime_get_raw_ts64(&tsf_ts); + tsf_ts = ktime_get_raw(); tsf = ath9k_hw_gettsf64(ah); saveLedState = REG_READ(ah, AR_CFG_LED) & @@ -1951,7 +1942,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } /* Restore TSF */ - tsf_offset = ath9k_hw_get_tsf_offset(&tsf_ts, NULL); + tsf_offset = ath9k_hw_get_tsf_offset(tsf_ts, 0); ath9k_hw_settsf64(ah, tsf + tsf_offset); if (AR_SREV_9280_20_OR_LATER(ah)) @@ -1975,7 +1966,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, * value after the initvals have been applied. */ if (AR_SREV_9100(ah) && (ath9k_hw_gettsf64(ah) < tsf)) { - tsf_offset = ath9k_hw_get_tsf_offset(&tsf_ts, NULL); + tsf_offset = ath9k_hw_get_tsf_offset(tsf_ts, 0); ath9k_hw_settsf64(ah, tsf + tsf_offset); } @@ -2149,7 +2140,7 @@ static void ath9k_set_power_network_sleep(struct ath_hw *ah) /* When chip goes into network sleep, it could be waken * up by MCI_INT interrupt caused by BT's HW messages - * (LNA_xxx, CONT_xxx) which chould be in a very fast + * (LNA_xxx, CONT_xxx) which could be in a very fast * rate (~100us). This will cause chip to leave and * re-enter network sleep mode frequently, which in * consequence will have WLAN MCI HW to generate lots of @@ -2544,7 +2535,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK); /* - * For AR9271 we will temporarilly uses the rx chainmax as read from + * For AR9271 we will temporarily use the rx chainmax as read from * the EEPROM. */ if ((ah->hw_version.devid == AR5416_DEVID_PCI) && @@ -2732,7 +2723,7 @@ static void ath9k_hw_gpio_cfg_soc(struct ath_hw *ah, u32 gpio, bool out, if (ah->caps.gpio_requested & BIT(gpio)) return; - err = gpio_request_one(gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label); + err = devm_gpio_request_one(ah->dev, gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label); if (err) { ath_err(ath9k_hw_common(ah), "request GPIO%d failed:%d\n", gpio, err); @@ -2801,10 +2792,8 @@ void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio) WARN_ON(gpio >= ah->caps.num_gpio_pins); - if (ah->caps.gpio_requested & BIT(gpio)) { - gpio_free(gpio); + if (ah->caps.gpio_requested & BIT(gpio)) ah->caps.gpio_requested &= ~BIT(gpio); - } } EXPORT_SYMBOL(ath9k_hw_gpio_free); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 450ab19b1d4e..eaa07d6dbde0 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -282,7 +282,7 @@ enum ath9k_hw_caps { * an exact user defined pattern or de-authentication/disassoc pattern. * @ATH9K_HW_WOW_PATTERN_MATCH_DWORD: device requires the first four * bytes of the pattern for user defined pattern, de-authentication and - * disassociation patterns for all types of possible frames recieved + * disassociation patterns for all types of possible frames received * of those types. */ @@ -1066,7 +1066,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); -u32 ath9k_hw_get_tsf_offset(struct timespec64 *last, struct timespec64 *cur); +u32 ath9k_hw_get_tsf_offset(ktime_t last, ktime_t cur); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set); void ath9k_hw_init_global_settings(struct ath_hw *ah); u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 7fad7e75af6a..ee951493e993 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -18,7 +18,6 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> -#include <linux/ath9k_platform.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_net.h> @@ -583,8 +582,8 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) /* nvmem cell might not be defined, or the nvmem * subsystem isn't included. In this case, follow - * the established "just return 0;" convention of - * ath9k_init_platform to say: + * the established "just return 0;" convention + * to say: * "All good. Nothing to see here. Please go on." */ if (err == -ENOENT || err == -EOPNOTSUPP) @@ -620,49 +619,6 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) return 0; } -static int ath9k_init_platform(struct ath_softc *sc) -{ - struct ath9k_platform_data *pdata = sc->dev->platform_data; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - int ret; - - if (!pdata) - return 0; - - if (!pdata->use_eeprom) { - ah->ah_flags &= ~AH_USE_EEPROM; - ah->gpio_mask = pdata->gpio_mask; - ah->gpio_val = pdata->gpio_val; - ah->led_pin = pdata->led_pin; - ah->is_clk_25mhz = pdata->is_clk_25mhz; - ah->get_mac_revision = pdata->get_mac_revision; - ah->external_reset = pdata->external_reset; - ah->disable_2ghz = pdata->disable_2ghz; - ah->disable_5ghz = pdata->disable_5ghz; - - if (!pdata->endian_check) - ah->ah_flags |= AH_NO_EEP_SWAP; - } - - if (pdata->eeprom_name) { - ret = ath9k_eeprom_request(sc, pdata->eeprom_name); - if (ret) - return ret; - } - - if (pdata->led_active_high) - ah->config.led_active_high = true; - - if (pdata->tx_gain_buffalo) - ah->config.tx_gain_buffalo = true; - - if (pdata->macaddr) - ether_addr_copy(common->macaddr, pdata->macaddr); - - return 0; -} - static int ath9k_of_init(struct ath_softc *sc) { struct device_node *np = sc->dev->of_node; @@ -691,7 +647,9 @@ static int ath9k_of_init(struct ath_softc *sc) ah->ah_flags |= AH_NO_EEP_SWAP; } - of_get_mac_address(np, common->macaddr); + ret = of_get_mac_address(np, common->macaddr); + if (ret == -EPROBE_DEFER) + return ret; return 0; } @@ -748,10 +706,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, */ ath9k_init_pcoem_platform(sc); - ret = ath9k_init_platform(sc); - if (ret) - return ret; - ret = ath9k_of_init(sc); if (ret) return ret; @@ -1145,7 +1099,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); - del_timer_sync(&sc->sleep_timer); + timer_delete_sync(&sc->sleep_timer); ath9k_hw_deinit(sc->sc_ah); if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index d1e5767aab3c..5d7e3ddb6dbc 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -50,7 +50,36 @@ reset: "tx hung, resetting the chip\n"); ath9k_queue_reset(sc, RESET_TYPE_TX_HANG); return false; +} + +#define RX_INACTIVE_CHECK_INTERVAL (4 * MSEC_PER_SEC) + +static bool ath_hw_rx_inactive_check(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + u32 interval, count; + + interval = jiffies_to_msecs(jiffies - sc->rx_active_check_time); + count = sc->rx_active_count; + + if (interval < RX_INACTIVE_CHECK_INTERVAL) + return true; /* too soon to check */ + sc->rx_active_count = 0; + sc->rx_active_check_time = jiffies; + + /* Need at least one interrupt per second, and we should only react if + * we are within a factor two of the expected interval + */ + if (interval > RX_INACTIVE_CHECK_INTERVAL * 2 || + count >= interval / MSEC_PER_SEC) + return true; + + ath_dbg(common, RESET, + "RX inactivity detected. Schedule chip reset\n"); + ath9k_queue_reset(sc, RESET_TYPE_RX_INACTIVE); + + return false; } void ath_hw_check_work(struct work_struct *work) @@ -58,8 +87,8 @@ void ath_hw_check_work(struct work_struct *work) struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work.work); - if (!ath_hw_check(sc) || - !ath_tx_complete_check(sc)) + if (!ath_hw_check(sc) || !ath_tx_complete_check(sc) || + !ath_hw_rx_inactive_check(sc)) return; ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work, @@ -303,7 +332,7 @@ fail_paprd: */ void ath_ani_calibrate(struct timer_list *t) { - struct ath_common *common = from_timer(common, t, ani.timer); + struct ath_common *common = timer_container_of(common, t, ani.timer); struct ath_softc *sc = common->priv; struct ath_hw *ah = sc->sc_ah; bool longcal = false; @@ -443,7 +472,7 @@ void ath_stop_ani(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); ath_dbg(common, ANI, "Stopping ANI\n"); - del_timer_sync(&common->ani.timer); + timer_delete_sync(&common->ani.timer); } void ath_check_ani(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index f03d792732da..16203e7ecf29 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -251,7 +251,7 @@ struct ath_desc { * when the descriptor is specifically marked to generate * an interrupt with this flag. Descriptors should be * marked periodically to insure timely replenishing of the - * supply needed for sending frames. Defering interrupts + * supply needed for sending frames. Deferring interrupts * reduces system load and potentially allows more concurrent * work to be done but if done to aggressively can cause * senders to backup. When the hardware queue is left too diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a2943aaecb20..c56f4f3b8990 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -98,7 +98,7 @@ static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) void ath_ps_full_sleep(struct timer_list *t) { - struct ath_softc *sc = from_timer(sc, t, sleep_timer); + struct ath_softc *sc = timer_container_of(sc, t, sleep_timer); struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; bool reset; @@ -123,7 +123,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc) if (++sc->ps_usecount != 1) goto unlock; - del_timer_sync(&sc->sleep_timer); + timer_delete_sync(&sc->sleep_timer); power_mode = sc->sc_ah->power_mode; ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); @@ -135,8 +135,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc) if (power_mode != ATH9K_PM_AWAKE) { spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); - memset(&common->cc_survey, 0, sizeof(common->cc_survey)); - memset(&common->cc_ani, 0, sizeof(common->cc_ani)); + memset(&common->cc, 0, sizeof(common->cc)); spin_unlock(&common->cc_lock); } @@ -250,8 +249,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) if (sc->cur_chan->tsf_val) { u32 offset; - offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, - NULL); + offset = ath9k_hw_get_tsf_offset(sc->cur_chan->tsf_ts, 0); ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset); } @@ -454,6 +452,7 @@ void ath9k_tasklet(struct tasklet_struct *t) ath_rx_tasklet(sc, 0, true); ath_rx_tasklet(sc, 0, false); + sc->rx_active_count++; } if (status & ATH9K_INT_TX) { @@ -896,7 +895,7 @@ static void ath9k_pending_key_del(struct ath_softc *sc, u8 keyix) ath_key_delete(common, keyix); } -static void ath9k_stop(struct ieee80211_hw *hw) +static void ath9k_stop(struct ieee80211_hw *hw, bool suspend) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; @@ -1002,7 +1001,7 @@ static bool ath9k_uses_beacons(int type) static void ath9k_vif_iter_set_beacon(struct ath9k_vif_iter_data *iter_data, struct ieee80211_vif *vif) { - /* Use the first (configured) interface, but prefering AP interfaces. */ + /* Use the first (configured) interface, but preferring AP interfaces. */ if (!iter_data->primary_beacon_vif) { iter_data->primary_beacon_vif = vif; } else { @@ -1956,7 +1955,7 @@ static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) tsf = ath9k_hw_gettsf64(sc->sc_ah); } else { tsf = sc->cur_chan->tsf_val + - ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); + ath9k_hw_get_tsf_offset(sc->cur_chan->tsf_ts, 0); } tsf += le64_to_cpu(avp->tsf_adjust); ath9k_ps_restore(sc); @@ -1975,7 +1974,7 @@ static void ath9k_set_tsf(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); tsf -= le64_to_cpu(avp->tsf_adjust); - ktime_get_raw_ts64(&avp->chanctx->tsf_ts); + avp->chanctx->tsf_ts = ktime_get_raw(); if (sc->cur_chan == avp->chanctx) ath9k_hw_settsf64(sc->sc_ah, tsf); avp->chanctx->tsf_val = tsf; @@ -1991,7 +1990,7 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); - ktime_get_raw_ts64(&avp->chanctx->tsf_ts); + avp->chanctx->tsf_ts = ktime_get_raw(); if (sc->cur_chan == avp->chanctx) ath9k_hw_reset_tsf(sc->sc_ah); avp->chanctx->tsf_val = 0; @@ -2419,7 +2418,7 @@ static void ath9k_cancel_pending_offchannel(struct ath_softc *sc) ath_dbg(common, CHAN_CTX, "%s: Aborting RoC\n", __func__); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) ath_roc_complete(sc, ATH_ROC_COMPLETE_ABORT); } @@ -2428,7 +2427,7 @@ static void ath9k_cancel_pending_offchannel(struct ath_softc *sc) ath_dbg(common, CHAN_CTX, "%s: Aborting HW scan\n", __func__); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); ath_scan_complete(sc, true); } } @@ -2477,7 +2476,7 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr); mutex_lock(&sc->mutex); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); ath_scan_complete(sc, true); mutex_unlock(&sc->mutex); } @@ -2527,7 +2526,7 @@ static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath_dbg(common, CHAN_CTX, "Cancel RoC\n"); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); if (sc->offchannel.roc_vif) { if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) @@ -2768,7 +2767,7 @@ void ath9k_fill_chanctx_ops(void) #endif static int ath9k_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - int *dbm) + unsigned int link_id, int *dbm) { struct ath_softc *sc = hw->priv; struct ath_vif *avp = (void *)vif->drv_priv; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index e655cd8bbf94..27d4034c814e 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -21,8 +21,6 @@ #include <linux/module.h> #include "ath9k.h" -extern int ath9k_use_msi; - static const struct pci_device_id ath_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ @@ -1031,7 +1029,7 @@ static int ath_pci_suspend(struct device *device) */ ath9k_stop_btcoex(sc); ath9k_hw_disable(sc->sc_ah); - del_timer_sync(&sc->sleep_timer); + timer_delete_sync(&sc->sleep_timer); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); return 0; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 0c0624a3b40d..34c74ed99b7b 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1042,8 +1042,8 @@ static void ath_rx_count_airtime(struct ath_softc *sc, if (!!(rxs->encoding == RX_ENC_HT)) { /* MCS rates */ - airtime += ath_pkt_duration(sc, rxs->rate_idx, len, - is_40, is_sgi, is_sp); + airtime += ath_pkt_duration(rxs->rate_idx, len, + is_40, is_sgi, is_sp); } else { phy = IS_CCK_RATE(rs->rs_rate) ? WLAN_RC_PHY_CCK : WLAN_RC_PHY_OFDM; diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 8d0b1730a9d5..ed4152cd44f0 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -60,7 +60,7 @@ static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE); /* - * Create Dissassociate / Deauthenticate packet filter + * Create Disassociate / Deauthenticate packet filter * * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes * +--------------+----------+---------+--------+--------+---- @@ -70,7 +70,7 @@ static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) * The above is the management frame format for disassociate/ * deauthenticate pattern, from this we need to match the first byte * of 'Frame Control' and DA, SA, and BSSID fields - * (skipping 2nd byte of FC and Duration feild. + * (skipping 2nd byte of FC and Duration field. * * Disassociate pattern * -------------------- @@ -225,7 +225,7 @@ int ath9k_suspend(struct ieee80211_hw *hw, ath9k_stop_btcoex(sc); /* - * Enable wake up on recieving disassoc/deauth + * Enable wake up on receiving disassoc/deauth * frame by default. */ ret = ath9k_wow_add_disassoc_deauth_pattern(sc); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index d519b676a109..0ac9212e42f7 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -67,8 +67,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, int nframes, int nbad, int txok); -static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, - struct ath_buf *bf); +static void ath_tx_update_baw(struct ath_atx_tid *tid, struct ath_buf *bf); static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, @@ -208,10 +207,10 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta, ARRAY_SIZE(bf->rates)); } -static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, - struct sk_buff *skb) +static void ath_txq_skb_done(struct ath_softc *sc, struct sk_buff *skb) { struct ath_frame_info *fi = get_frame_info(skb); + struct ath_txq *txq; int q = fi->txq; if (q < 0) @@ -224,7 +223,7 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, } static struct ath_atx_tid * -ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb) +ath_get_skb_tid(struct ath_node *an, struct sk_buff *skb) { u8 tidno = skb->priority & IEEE80211_QOS_CTL_TID_MASK; return ATH_AN_2_TID(an, tidno); @@ -294,13 +293,13 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) fi = get_frame_info(skb); bf = fi->bf; if (!bf) { - ath_txq_skb_done(sc, txq, skb); + ath_txq_skb_done(sc, skb); ieee80211_free_txskb(sc->hw, skb); continue; } if (fi->baw_tracked) { - ath_tx_update_baw(sc, tid, bf); + ath_tx_update_baw(tid, bf); sendbar = true; } @@ -315,8 +314,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) } } -static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, - struct ath_buf *bf) +static void ath_tx_update_baw(struct ath_atx_tid *tid, struct ath_buf *bf) { struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu); u16 seqno = bf->bf_state.seqno; @@ -338,8 +336,7 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, } } -static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, - struct ath_buf *bf) +static void ath_tx_addto_baw(struct ath_atx_tid *tid, struct ath_buf *bf) { struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu); u16 seqno = bf->bf_state.seqno; @@ -452,9 +449,8 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) return tbf; } -static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf, - struct ath_tx_status *ts, int txok, - int *nframes, int *nbad) +static void ath_tx_count_frames(struct ath_buf *bf, struct ath_tx_status *ts, + int txok, int *nframes, int *nbad) { u16 seq_st = 0; u32 ba[WME_BA_BMP_SIZE >> 5]; @@ -557,7 +553,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, /* * AR5416 can become deaf/mute when BA * issue happens. Chip needs to be reset. - * But AP code may have sychronization issues + * But AP code may have synchronization issues * when perform internal reset in this routine. * Only enable reset in STA mode for now. */ @@ -568,7 +564,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, __skb_queue_head_init(&bf_pending); - ath_tx_count_frames(sc, bf, ts, txok, &nframes, &nbad); + ath_tx_count_frames(bf, ts, txok, &nframes, &nbad); while (bf) { u16 seqno = bf->bf_state.seqno; @@ -621,7 +617,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, * complete the acked-ones/xretried ones; update * block-ack window */ - ath_tx_update_baw(sc, tid, bf); + ath_tx_update_baw(tid, bf); if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { memcpy(tx_info->control.rates, rates, sizeof(rates)); @@ -651,7 +647,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, * run out of tx buf. */ if (!tbf) { - ath_tx_update_baw(sc, tid, bf); + ath_tx_update_baw(tid, bf); ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, ts, @@ -752,7 +748,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); if (sta) { struct ath_node *an = (struct ath_node *)sta->drv_priv; - tid = ath_get_skb_tid(sc, an, bf->bf_mpdu); + tid = ath_get_skb_tid(an, bf->bf_mpdu); ath_tx_count_airtime(sc, sta, bf, ts, tid->tidno); if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) tid->clear_ps_filter = true; @@ -962,7 +958,7 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, bf->bf_state.stale = false; if (!bf) { - ath_txq_skb_done(sc, txq, skb); + ath_txq_skb_done(sc, skb); ieee80211_free_txskb(sc->hw, skb); continue; } @@ -1012,13 +1008,13 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, INIT_LIST_HEAD(&bf_head); list_add(&bf->list, &bf_head); - ath_tx_update_baw(sc, tid, bf); + ath_tx_update_baw(tid, bf); ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0); continue; } if (bf_isampdu(bf)) - ath_tx_addto_baw(sc, tid, bf); + ath_tx_addto_baw(tid, bf); break; } @@ -1114,8 +1110,8 @@ finish: * width - 0 for 20 MHz, 1 for 40 MHz * half_gi - to use 4us v/s 3.6 us for symbol time */ -u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, - int width, int half_gi, bool shortPreamble) +u32 ath_pkt_duration(u8 rix, int pktlen, int width, + int half_gi, bool shortPreamble) { u32 nbits, nsymbits, duration, nsymbols; int streams; @@ -1327,7 +1323,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, info->rates[i].Rate = rix | 0x80; info->rates[i].ChSel = ath_txchainmask_reduction(sc, ah->txchainmask, info->rates[i].Rate); - info->rates[i].PktDuration = ath_pkt_duration(sc, rix, len, + info->rates[i].PktDuration = ath_pkt_duration(rix, len, is_40, is_sgi, is_sp); if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC)) info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC; @@ -1674,8 +1670,14 @@ static void ath9k_set_moredata(struct ath_softc *sc, struct ath_buf *bf, bool val) { struct ieee80211_hdr *hdr; - u16 mask = cpu_to_le16(IEEE80211_FCTL_MOREDATA); - u16 mask_val = mask * val; + __le16 mask, mask_val; + + mask = cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + if (val) + mask_val = mask; + else + mask_val = 0; hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data; if ((hdr->frame_control & mask) != mask_val) { @@ -2116,7 +2118,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, bf->bf_state.bf_type = 0; if (tid && (tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { bf->bf_state.bf_type = BUF_AMPDU; - ath_tx_addto_baw(sc, tid, bf); + ath_tx_addto_baw(tid, bf); } bf->bf_next = NULL; @@ -2289,19 +2291,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; - struct ath_vif *avp; struct ath_softc *sc = hw->priv; int frmlen = skb->len + FCS_LEN; int padpos, padsize; - /* NOTE: sta can be NULL according to net/mac80211.h */ - if (sta) - txctl->an = (struct ath_node *)sta->drv_priv; - else if (vif && ieee80211_is_data(hdr->frame_control)) { - avp = (void *)vif->drv_priv; - txctl->an = &avp->mcast_node; - } - if (info->control.hw_key) frmlen += info->control.hw_key->icv_len; @@ -2362,7 +2355,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (txctl->sta) { an = (struct ath_node *) sta->drv_priv; - tid = ath_get_skb_tid(sc, an, skb); + tid = ath_get_skb_tid(an, skb); } ath_txq_lock(sc, txq); @@ -2373,7 +2366,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, bf = ath_tx_setup_buffer(sc, txq, tid, skb); if (!bf) { - ath_txq_skb_done(sc, txq, skb); + ath_txq_skb_done(sc, skb); if (txctl->paprd) dev_kfree_skb_any(skb); else @@ -2508,7 +2501,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); - ath_txq_skb_done(sc, txq, skb); + ath_txq_skb_done(sc, skb); tx_info->status.status_driver_data[0] = sta; __skb_queue_tail(&txq->complete_q, skb); } diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index bb40889d7c72..2d734567000a 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -54,7 +54,6 @@ struct carl9170_debugfs_fops { char *(*read)(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len); ssize_t (*write)(struct ar9170 *aru, const char *buf, size_t size); - const struct file_operations fops; enum carl9170_device_state req_dev_state; }; @@ -62,7 +61,7 @@ struct carl9170_debugfs_fops { static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - struct carl9170_debugfs_fops *dfops; + const struct carl9170_debugfs_fops *dfops; struct ar9170 *ar; char *buf = NULL, *res_buf = NULL; ssize_t ret = 0; @@ -75,8 +74,7 @@ static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf, if (!ar) return -ENODEV; - dfops = container_of(debugfs_real_fops(file), - struct carl9170_debugfs_fops, fops); + dfops = debugfs_get_aux(file); if (!dfops->read) return -ENOSYS; @@ -113,7 +111,7 @@ out_free: static ssize_t carl9170_debugfs_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { - struct carl9170_debugfs_fops *dfops; + const struct carl9170_debugfs_fops *dfops; struct ar9170 *ar; char *buf = NULL; int err = 0; @@ -128,8 +126,7 @@ static ssize_t carl9170_debugfs_write(struct file *file, if (!ar) return -ENODEV; - dfops = container_of(debugfs_real_fops(file), - struct carl9170_debugfs_fops, fops); + dfops = debugfs_get_aux(file); if (!dfops->write) return -ENOSYS; @@ -165,6 +162,11 @@ out_free: return err; } +static struct debugfs_short_fops debugfs_fops = { + .read = carl9170_debugfs_read, + .write = carl9170_debugfs_write, +}; + #define __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, \ _attr, _dstate) \ static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\ @@ -173,12 +175,6 @@ static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\ .write = _write, \ .attr = _attr, \ .req_dev_state = _dstate, \ - .fops = { \ - .open = simple_open, \ - .read = carl9170_debugfs_read, \ - .write = carl9170_debugfs_write, \ - .owner = THIS_MODULE \ - }, \ } #define DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, _attr) \ @@ -816,9 +812,9 @@ void carl9170_debugfs_register(struct ar9170 *ar) ar->hw->wiphy->debugfsdir); #define DEBUGFS_ADD(name) \ - debugfs_create_file(#name, carl_debugfs_##name ##_ops.attr, \ - ar->debug_dir, ar, \ - &carl_debugfs_##name ## _ops.fops) + debugfs_create_file_aux(#name, carl_debugfs_##name ##_ops.attr, \ + ar->debug_dir, ar, &carl_debugfs_##name ## _ops, \ + &debugfs_fops) DEBUGFS_ADD(usb_tx_anch_urbs); DEBUGFS_ADD(usb_rx_pool_urbs); diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 4c1aecd1163c..419f5530f885 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -15,7 +15,7 @@ #include "fwcmd.h" #include "version.h" -static const u8 otus_magic[4] = { OTUS_MAGIC }; +static const u8 otus_magic[4] __nonstring = { OTUS_MAGIC }; static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], const unsigned int len, const u8 compatible_revision) diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c index 6cdbee5beb07..20ceed0dd4be 100644 --- a/drivers/net/wireless/ath/carl9170/mac.c +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -36,7 +36,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "carl9170.h" #include "cmd.h" diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 7e7797bf44b7..755c068e4197 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -439,7 +439,7 @@ static void carl9170_cancel_worker(struct ar9170 *ar) cancel_work_sync(&ar->ampdu_work); } -static void carl9170_op_stop(struct ieee80211_hw *hw) +static void carl9170_op_stop(struct ieee80211_hw *hw, bool suspend) { struct ar9170 *ar = hw->priv; diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index e902ca80eba7..b7717f9e1e9b 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -280,7 +280,8 @@ static void carl9170_tx_release(struct kref *ref) * carl9170_tx_fill_rateinfo() has filled the rate information * before we get to this point. */ - memset_after(&txinfo->status, 0, rates); + memset(&txinfo->pad, 0, sizeof(txinfo->pad)); + memset(&txinfo->rate_driver_data, 0, sizeof(txinfo->rate_driver_data)); if (atomic_read(&ar->tx_total_queued)) ar->tx_schedule = true; @@ -365,8 +366,7 @@ static void carl9170_tx_shift_bm(struct ar9170 *ar, if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS)) return; - if (!bitmap_empty(tid_info->bitmap, off)) - off = find_first_bit(tid_info->bitmap, off); + off = min(off, find_first_bit(tid_info->bitmap, off)); tid_info->bsn += off; tid_info->bsn &= 0x0fff; diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index c4edf8355941..564ca6a61985 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -438,14 +438,21 @@ static void carl9170_usb_rx_complete(struct urb *urb) if (atomic_read(&ar->rx_anch_urbs) == 0) { /* - * The system is too slow to cope with - * the enormous workload. We have simply - * run out of active rx urbs and this - * unfortunately leads to an unpredictable - * device. + * At this point, either the system is too slow to + * cope with the enormous workload (so we have simply + * run out of active rx urbs and this unfortunately + * leads to an unpredictable device), or the device + * is not fully functional after an unsuccessful + * firmware loading attempts (so it doesn't pass + * ieee80211_register_hw() and there is no internal + * workqueue at all). */ - ieee80211_queue_work(ar->hw, &ar->ping_work); + if (ar->registered) + ieee80211_queue_work(ar->hw, &ar->ping_work); + else + pr_warn_once("device %s is not registered\n", + dev_name(&ar->udev->dev)); } } else { /* @@ -1069,6 +1076,38 @@ static int carl9170_usb_probe(struct usb_interface *intf, ar->usb_ep_cmd_is_bulk = true; } + /* Verify that all expected endpoints are present */ + if (ar->usb_ep_cmd_is_bulk) { + u8 bulk_ep_addr[] = { + AR9170_USB_EP_RX | USB_DIR_IN, + AR9170_USB_EP_TX | USB_DIR_OUT, + AR9170_USB_EP_CMD | USB_DIR_OUT, + 0}; + u8 int_ep_addr[] = { + AR9170_USB_EP_IRQ | USB_DIR_IN, + 0}; + if (!usb_check_bulk_endpoints(intf, bulk_ep_addr) || + !usb_check_int_endpoints(intf, int_ep_addr)) + err = -ENODEV; + } else { + u8 bulk_ep_addr[] = { + AR9170_USB_EP_RX | USB_DIR_IN, + AR9170_USB_EP_TX | USB_DIR_OUT, + 0}; + u8 int_ep_addr[] = { + AR9170_USB_EP_IRQ | USB_DIR_IN, + AR9170_USB_EP_CMD | USB_DIR_OUT, + 0}; + if (!usb_check_bulk_endpoints(intf, bulk_ep_addr) || + !usb_check_int_endpoints(intf, int_ep_addr)) + err = -ENODEV; + } + + if (err) { + carl9170_free(ar); + return err; + } + usb_set_intfdata(intf, ar); SET_IEEE80211_DEV(ar->hw, &intf->dev); diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c index 85955572a705..b301e6fbce6c 100644 --- a/drivers/net/wireless/ath/hw.c +++ b/drivers/net/wireless/ath/hw.c @@ -15,7 +15,7 @@ */ #include <linux/export.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "ath.h" #include "reg.h" diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c index 21a93fec284d..0ae436bd9b66 100644 --- a/drivers/net/wireless/ath/key.c +++ b/drivers/net/wireless/ath/key.c @@ -16,7 +16,7 @@ */ #include <linux/export.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <net/mac80211.h> #include "ath.h" diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/testmode_i.h index 91b83873d660..980ef2f3f05f 100644 --- a/drivers/net/wireless/ath/ath11k/testmode_i.h +++ b/drivers/net/wireless/ath/testmode_i.h @@ -1,59 +1,59 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ -/* "API" level of the ath11k testmode interface. Bump it after every +/* "API" level of the ath testmode interface. Bump it after every * incompatible interface change. */ -#define ATH11K_TESTMODE_VERSION_MAJOR 1 +#define ATH_TESTMODE_VERSION_MAJOR 1 /* Bump this after every _compatible_ interface change, for example * addition of a new command or an attribute. */ -#define ATH11K_TESTMODE_VERSION_MINOR 1 +#define ATH_TESTMODE_VERSION_MINOR 1 -#define ATH11K_TM_DATA_MAX_LEN 5000 -#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048 +#define ATH_TM_DATA_MAX_LEN 5000 +#define ATH_FTM_EVENT_MAX_BUF_LENGTH 2048 -enum ath11k_tm_attr { - __ATH11K_TM_ATTR_INVALID = 0, - ATH11K_TM_ATTR_CMD = 1, - ATH11K_TM_ATTR_DATA = 2, - ATH11K_TM_ATTR_WMI_CMDID = 3, - ATH11K_TM_ATTR_VERSION_MAJOR = 4, - ATH11K_TM_ATTR_VERSION_MINOR = 5, - ATH11K_TM_ATTR_WMI_OP_VERSION = 6, +enum ath_tm_attr { + __ATH_TM_ATTR_INVALID = 0, + ATH_TM_ATTR_CMD = 1, + ATH_TM_ATTR_DATA = 2, + ATH_TM_ATTR_WMI_CMDID = 3, + ATH_TM_ATTR_VERSION_MAJOR = 4, + ATH_TM_ATTR_VERSION_MINOR = 5, + ATH_TM_ATTR_WMI_OP_VERSION = 6, /* keep last */ - __ATH11K_TM_ATTR_AFTER_LAST, - ATH11K_TM_ATTR_MAX = __ATH11K_TM_ATTR_AFTER_LAST - 1, + __ATH_TM_ATTR_AFTER_LAST, + ATH_TM_ATTR_MAX = __ATH_TM_ATTR_AFTER_LAST - 1, }; -/* All ath11k testmode interface commands specified in - * ATH11K_TM_ATTR_CMD +/* All ath testmode interface commands specified in + * ATH_TM_ATTR_CMD */ -enum ath11k_tm_cmd { - /* Returns the supported ath11k testmode interface version in - * ATH11K_TM_ATTR_VERSION. Always guaranteed to work. User space +enum ath_tm_cmd { + /* Returns the supported ath testmode interface version in + * ATH_TM_ATTR_VERSION. Always guaranteed to work. User space * uses this to verify it's using the correct version of the * testmode interface */ - ATH11K_TM_CMD_GET_VERSION = 0, + ATH_TM_CMD_GET_VERSION = 0, /* The command used to transmit a WMI command to the firmware and * the event to receive WMI events from the firmware. Without * struct wmi_cmd_hdr header, only the WMI payload. Command id is - * provided with ATH11K_TM_ATTR_WMI_CMDID and payload in - * ATH11K_TM_ATTR_DATA. + * provided with ATH_TM_ATTR_WMI_CMDID and payload in + * ATH_TM_ATTR_DATA. */ - ATH11K_TM_CMD_WMI = 1, + ATH_TM_CMD_WMI = 1, /* Boots the UTF firmware, the netdev interface must be down at the * time. */ - ATH11K_TM_CMD_TESTMODE_START = 2, + ATH_TM_CMD_TESTMODE_START = 2, /* The command used to transmit a FTM WMI command to the firmware * and the event to receive WMI events from the firmware. The data @@ -62,5 +62,5 @@ enum ath11k_tm_cmd { * The data payload size could be large and the driver needs to * send segmented data to firmware. */ - ATH11K_TM_CMD_WMI_FTM = 3, + ATH_TM_CMD_WMI_FTM = 3, }; diff --git a/drivers/net/wireless/ath/trace.h b/drivers/net/wireless/ath/trace.h index 9935cf475b6d..82aac0a4baff 100644 --- a/drivers/net/wireless/ath/trace.h +++ b/drivers/net/wireless/ath/trace.h @@ -44,8 +44,8 @@ TRACE_EVENT(ath_log, ), TP_fast_assign( - __assign_str(device, wiphy_name(wiphy)); - __assign_str(driver, KBUILD_MODNAME); + __assign_str(device); + __assign_str(driver); __assign_vstr(msg, vaf->fmt, vaf->va); ), diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index d405a4c34059..0f4df5585fd9 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -350,7 +350,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) spin_lock_irqsave(&wcn->dxe_lock, flags); skb = wcn->tx_ack_skb; wcn->tx_ack_skb = NULL; - del_timer(&wcn->tx_ack_timer); + timer_delete(&wcn->tx_ack_timer); spin_unlock_irqrestore(&wcn->dxe_lock, flags); if (!skb) { @@ -373,7 +373,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) static void wcn36xx_dxe_tx_timer(struct timer_list *t) { - struct wcn36xx *wcn = from_timer(wcn, t, tx_ack_timer); + struct wcn36xx *wcn = timer_container_of(wcn, t, tx_ack_timer); struct ieee80211_tx_info *info; unsigned long flags; struct sk_buff *skb; @@ -1055,7 +1055,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn) free_irq(wcn->tx_irq, wcn); free_irq(wcn->rx_irq, wcn); - del_timer(&wcn->tx_ack_timer); + timer_delete(&wcn->tx_ack_timer); if (wcn->tx_ack_skb) { ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index bfbd3c7a70b3..94d08d6ae1a3 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -278,7 +278,7 @@ out_err: return ret; } -static void wcn36xx_stop(struct ieee80211_hw *hw) +static void wcn36xx_stop(struct ieee80211_hw *hw, bool suspend) { struct wcn36xx *wcn = hw->priv; @@ -756,9 +756,9 @@ static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, if (sta->deflink.vht_cap.vht_supported) { sta_priv->supported_rates.op_rate_mode = STA_11ac; sta_priv->supported_rates.vht_rx_mcs_map = - sta->deflink.vht_cap.vht_mcs.rx_mcs_map; + le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); sta_priv->supported_rates.vht_tx_mcs_map = - sta->deflink.vht_cap.vht_mcs.tx_mcs_map; + le16_to_cpu(sta->deflink.vht_cap.vht_mcs.tx_mcs_map); } } @@ -1590,7 +1590,10 @@ static int wcn36xx_probe(struct platform_device *pdev) } n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; - wcn->chan_survey = devm_kmalloc(wcn->dev, n_channels, GFP_KERNEL); + wcn->chan_survey = devm_kcalloc(wcn->dev, + n_channels, + sizeof(struct wcn36xx_chan_survey), + GFP_KERNEL); if (!wcn->chan_survey) { ret = -ENOMEM; goto out_wq; @@ -1679,10 +1682,10 @@ static const struct of_device_id wcn36xx_of_match[] = { MODULE_DEVICE_TABLE(of, wcn36xx_of_match); static struct platform_driver wcn36xx_driver = { - .probe = wcn36xx_probe, - .remove_new = wcn36xx_remove, - .driver = { - .name = "wcn36xx", + .probe = wcn36xx_probe, + .remove = wcn36xx_remove, + .driver = { + .name = "wcn36xx", .of_match_table = wcn36xx_of_match, }, }; diff --git a/drivers/net/wireless/ath/wcn36xx/testmode.c b/drivers/net/wireless/ath/wcn36xx/testmode.c index e5142c052985..d7a2a483cbc4 100644 --- a/drivers/net/wireless/ath/wcn36xx/testmode.c +++ b/drivers/net/wireless/ath/wcn36xx/testmode.c @@ -56,7 +56,7 @@ static int wcn36xx_tm_cmd_ptt(struct wcn36xx *wcn, struct ieee80211_vif *vif, msg = buf; wcn36xx_dbg(WCN36XX_DBG_TESTMODE, - "testmode cmd wmi msg_id 0x%04X msg_len %d buf %pK buf_len %d\n", + "testmode cmd wmi msg_id 0x%04X msg_len %d buf %p buf_len %d\n", msg->msg_id, msg->msg_body_length, buf, buf_len); diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index 0802ed728824..8826998797d6 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -318,7 +318,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) memset(&status, 0, sizeof(status)); bd = (struct wcn36xx_rx_bd *)skb->data; - buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); + buff_to_be(bd, sizeof(*bd)/sizeof(u32)); wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "BD <<< ", (char *)bd, sizeof(struct wcn36xx_rx_bd)); @@ -692,7 +692,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, /* MGMT and CTRL frames are handeld here*/ wcn36xx_set_tx_mgmt(&bd, wcn, &vif_priv, skb, bcast); - buff_to_be((u32 *)&bd, sizeof(bd)/sizeof(u32)); + buff_to_be(&bd, sizeof(bd)/sizeof(u32)); bd.tx_bd_sign = 0xbdbdbdbd; ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low); diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index ff4a8e5d7209..7ee79593cd23 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -100,11 +100,14 @@ enum wcn36xx_ampdu_state { #define RF_IRIS_WCN3660 0x3660 #define RF_IRIS_WCN3680 0x3680 -static inline void buff_to_be(u32 *buf, size_t len) +static inline void buff_to_be(void *buf, size_t len) { + __be32 *to = buf; + u32 *from = buf; int i; + for (i = 0; i < len; i++) - buf[i] = cpu_to_be32(buf[i]); + to[i] = cpu_to_be32(from[i]); } struct nv_data { @@ -164,7 +167,7 @@ struct wcn36xx_vif { * @dpu_desc_index: DPU descriptor index is returned from HW after config_sta * call and is used in TX BD. * @bss_sta_index: STA index is returned from HW after config_bss call and is - * used in both SMD channel and TX BD. See table bellow when it is used. + * used in both SMD channel and TX BD. See table below when it is used. * @bss_dpu_desc_index: DPU descriptor index is returned from HW after * config_bss call and is used in TX BD. * ______________________________________________ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index dbe4b3478f03..5473c01cbe66 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -892,10 +892,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wireless_dev *wdev = request->wdev; struct wil6210_vif *vif = wdev_to_vif(wil, wdev); - struct { - struct wmi_start_scan_cmd cmd; - u16 chnl[4]; - } __packed cmd; + DEFINE_FLEX(struct wmi_start_scan_cmd, cmd, + channel_list, num_channels, 4); uint i, n; int rc; @@ -977,9 +975,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, vif->scan_request = request; mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO); - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd.scan_type = WMI_ACTIVE_SCAN; - cmd.cmd.num_channels = 0; + cmd->scan_type = WMI_ACTIVE_SCAN; + cmd->num_channels = 0; n = min(request->n_channels, 4U); for (i = 0; i < n; i++) { int ch = request->channels[i]->hw_value; @@ -991,7 +988,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, continue; } /* 0-based channel indexes */ - cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; + cmd->num_channels++; + cmd->channel_list[cmd->num_channels - 1].channel = ch - 1; wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch, request->channels[i]->center_freq); } @@ -1007,20 +1005,19 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, if (rc) goto out_restore; - if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) { - cmd.cmd.discovery_mode = 1; + if (wil->discovery_mode && cmd->scan_type == WMI_ACTIVE_SCAN) { + cmd->discovery_mode = 1; wil_dbg_misc(wil, "active scan with discovery_mode=1\n"); } if (vif->mid == 0) wil->radio_wdev = wdev; rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid, - &cmd, sizeof(cmd.cmd) + - cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); + cmd, struct_size(cmd, channel_list, cmd->num_channels)); out_restore: if (rc) { - del_timer_sync(&vif->scan_timer); + timer_delete_sync(&vif->scan_timer); if (vif->mid == 0) wil->radio_wdev = wil->main_ndev->ieee80211_ptr; vif->scan_request = NULL; @@ -1496,6 +1493,7 @@ out: } static int wil_cfg80211_set_channel(struct wiphy *wiphy, + struct net_device *dev, struct cfg80211_chan_def *chandef) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); @@ -2735,7 +2733,7 @@ int wil_cfg80211_iface_combinations_from_fw( return 0; } - combo = conc->combos; + combo = (const struct wil_fw_concurrency_combo *)(conc + 1); n_combos = le16_to_cpu(conc->n_combos); for (i = 0; i < n_combos; i++) { total_limits += combo->n_limits; @@ -2751,7 +2749,7 @@ int wil_cfg80211_iface_combinations_from_fw( return -ENOMEM; iface_limit = (struct ieee80211_iface_limit *)(iface_combinations + n_combos); - combo = conc->combos; + combo = (const struct wil_fw_concurrency_combo *)(conc + 1); for (i = 0; i < n_combos; i++) { iface_combinations[i].max_interfaces = combo->max_interfaces; iface_combinations[i].num_different_channels = diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h index aa1620e0d24f..2079a90ec260 100644 --- a/drivers/net/wireless/ath/wil6210/fw.h +++ b/drivers/net/wireless/ath/wil6210/fw.h @@ -93,7 +93,6 @@ struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */ /* number of concurrency combinations that follow */ __le16 n_combos; /* keep last - combinations, variable size by n_combos */ - struct wil_fw_concurrency_combo combos[]; } __packed; /* brd file info encoded inside a comment record */ diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index fbc84c03406b..c3c0b289dcf3 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -212,8 +212,8 @@ fw_handle_concurrency(struct wil6210_priv *wil, const void *data, } n_combos = le16_to_cpu(rec->n_combos); - remain = size - offsetof(struct wil_fw_record_concurrency, combos); - combo = rec->combos; + remain = size - sizeof(struct wil_fw_record_concurrency); + combo = (const struct wil_fw_concurrency_combo *)(rec + 1); for (i = 0; i < n_combos; i++) { if (remain < sizeof(*combo)) goto out_short; diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 67172385a5d6..89d4394cedcf 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -179,9 +179,11 @@ void wil_mask_irq(struct wil6210_priv *wil) wil_dbg_irq(wil, "mask_irq\n"); wil6210_mask_irq_tx(wil); - wil6210_mask_irq_tx_edma(wil); + if (wil->use_enhanced_dma_hw) + wil6210_mask_irq_tx_edma(wil); wil6210_mask_irq_rx(wil); - wil6210_mask_irq_rx_edma(wil); + if (wil->use_enhanced_dma_hw) + wil6210_mask_irq_rx_edma(wil); wil6210_mask_irq_misc(wil, true); wil6210_mask_irq_pseudo(wil); } @@ -190,10 +192,12 @@ void wil_unmask_irq(struct wil6210_priv *wil) { wil_dbg_irq(wil, "unmask_irq\n"); - wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC), - WIL_ICR_ICC_VALUE); - wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC), - WIL_ICR_ICC_VALUE); + if (wil->use_enhanced_dma_hw) { + wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC), + WIL_ICR_ICC_VALUE); + wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC), + WIL_ICR_ICC_VALUE); + } wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC), WIL_ICR_ICC_MISC_VALUE); wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC), @@ -845,10 +849,12 @@ void wil6210_clear_irq(struct wil6210_priv *wil) offsetof(struct RGF_ICR, ICR)); wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, ICR)); - wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) + - offsetof(struct RGF_ICR, ICR)); - wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) + - offsetof(struct RGF_ICR, ICR)); + if (wil->use_enhanced_dma_hw) { + wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + } wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICR)); wmb(); /* make sure write completed */ diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 94e61dbe94f8..44c24c6c8360 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -798,7 +798,7 @@ void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, wil_dbg_misc(wil, "disconnecting\n"); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); _wil6210_disconnect(vif, bssid, reason_code); } @@ -818,7 +818,7 @@ void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid, wil_dbg_misc(wil, "got disconnect\n"); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); _wil6210_disconnect_complete(vif, bssid, reason_code); } @@ -1465,7 +1465,7 @@ void wil_abort_scan(struct wil6210_vif *vif, bool sync) return; wil_dbg_misc(wil, "Abort scan_request 0x%p\n", vif->scan_request); - del_timer_sync(&vif->scan_timer); + timer_delete_sync(&vif->scan_timer); mutex_unlock(&wil->vif_mutex); rc = wmi_abort_scan(vif); if (!rc && sync) diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index ee7d7e9c2718..55c267ab2893 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -200,8 +200,8 @@ static void wil_dev_setup(struct net_device *dev) static void wil_vif_deinit(struct wil6210_vif *vif) { - del_timer_sync(&vif->scan_timer); - del_timer_sync(&vif->p2p.discovery_timer); + timer_delete_sync(&vif->scan_timer); + timer_delete_sync(&vif->p2p.discovery_timer); cancel_work_sync(&vif->disconnect_worker); cancel_work_sync(&vif->p2p.discovery_expired_work); cancel_work_sync(&vif->p2p.delayed_listen_work); @@ -227,7 +227,7 @@ static void wil_ndev_destructor(struct net_device *ndev) static void wil_connect_timer_fn(struct timer_list *t) { - struct wil6210_vif *vif = from_timer(vif, t, connect_timer); + struct wil6210_vif *vif = timer_container_of(vif, t, connect_timer); struct wil6210_priv *wil = vif_to_wil(vif); bool q; @@ -243,7 +243,7 @@ static void wil_connect_timer_fn(struct timer_list *t) static void wil_scan_timer_fn(struct timer_list *t) { - struct wil6210_vif *vif = from_timer(vif, t, scan_timer); + struct wil6210_vif *vif = timer_container_of(vif, t, scan_timer); struct wil6210_priv *wil = vif_to_wil(vif); clear_bit(wil_status_fwready, wil->status); @@ -253,7 +253,8 @@ static void wil_scan_timer_fn(struct timer_list *t) static void wil_p2p_discovery_timer_fn(struct timer_list *t) { - struct wil6210_vif *vif = from_timer(vif, t, p2p.discovery_timer); + struct wil6210_vif *vif = timer_container_of(vif, t, + p2p.discovery_timer); struct wil6210_priv *wil = vif_to_wil(vif); wil_dbg_misc(wil, "p2p_discovery_timer_fn\n"); @@ -453,16 +454,21 @@ int wil_if_add(struct wil6210_priv *wil) return rc; } - init_dummy_netdev(&wil->napi_ndev); + wil->napi_ndev = alloc_netdev_dummy(0); + if (!wil->napi_ndev) { + wil_err(wil, "failed to allocate dummy netdev"); + rc = -ENOMEM; + goto out_wiphy; + } if (wil->use_enhanced_dma_hw) { - netif_napi_add(&wil->napi_ndev, &wil->napi_rx, + netif_napi_add(wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx_edma); - netif_napi_add_tx(&wil->napi_ndev, + netif_napi_add_tx(wil->napi_ndev, &wil->napi_tx, wil6210_netdev_poll_tx_edma); } else { - netif_napi_add(&wil->napi_ndev, &wil->napi_rx, + netif_napi_add(wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx); - netif_napi_add_tx(&wil->napi_ndev, + netif_napi_add_tx(wil->napi_ndev, &wil->napi_tx, wil6210_netdev_poll_tx); } @@ -474,10 +480,12 @@ int wil_if_add(struct wil6210_priv *wil) wiphy_unlock(wiphy); rtnl_unlock(); if (rc < 0) - goto out_wiphy; + goto free_dummy; return 0; +free_dummy: + free_netdev(wil->napi_ndev); out_wiphy: wiphy_unregister(wiphy); return rc; @@ -526,7 +534,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid) mutex_unlock(&wil->vif_mutex); flush_work(&wil->wmi_event_worker); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); cancel_work_sync(&vif->disconnect_worker); wil_probe_client_flush(vif); cancel_work_sync(&vif->probe_client_worker); @@ -554,5 +562,7 @@ void wil_if_remove(struct wil6210_priv *wil) netif_napi_del(&wil->napi_tx); netif_napi_del(&wil->napi_rx); + free_netdev(wil->napi_ndev); + wiphy_unregister(wiphy); } diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index f26bf046d889..f20caf1a3905 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -184,7 +184,7 @@ u8 wil_p2p_stop_discovery(struct wil6210_vif *vif) /* discovery not really started, only pending */ p2p->pending_listen_wdev = NULL; } else { - del_timer_sync(&p2p->discovery_timer); + timer_delete_sync(&p2p->discovery_timer); wmi_stop_discovery(vif); } p2p->discovery_started = 0; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index f29ac6de7139..19702b6f09c3 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -306,7 +306,7 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, struct sk_buff *skb) { struct wil6210_rtap { - struct ieee80211_radiotap_header rthdr; + struct ieee80211_radiotap_header_fixed rthdr; /* fields should be in the order of bits in rthdr.it_present */ /* flags */ u8 flags; diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 689f68d89a44..fff8b7f8abc5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -7,6 +7,7 @@ #ifndef WIL6210_TXRX_H #define WIL6210_TXRX_H +#include <net/sock.h> #include "wil6210.h" #include "txrx_edma.h" @@ -616,8 +617,7 @@ static inline bool wil_need_txstat(struct sk_buff *skb) { const u8 *da = wil_skb_get_da(skb); - return is_unicast_ether_addr(da) && skb->sk && - (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS); + return is_unicast_ether_addr(da) && sk_requests_wifi_status(skb->sk); } static inline void wil_consume_skb(struct sk_buff *skb, bool acked) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 22a6eb3e12b7..9bd1286d2857 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -983,7 +983,7 @@ struct wil6210_priv { spinlock_t eap_lock; /* guarding access to eap rekey fields */ struct napi_struct napi_rx; struct napi_struct napi_tx; - struct net_device napi_ndev; /* dummy net_device serving all VIFs */ + struct net_device *napi_ndev; /* dummy net_device serving all VIFs */ /* DMA related */ struct wil_ring ring_rx; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 6fdb77d4c59e..74edd007cd8d 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -933,7 +933,7 @@ static void wmi_evt_scan_complete(struct wil6210_vif *vif, int id, wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status); wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", vif->scan_request, info.aborted); - del_timer_sync(&vif->scan_timer); + timer_delete_sync(&vif->scan_timer); cfg80211_scan_done(vif->scan_request, &info); if (vif->mid == 0) wil->radio_wdev = wil->main_ndev->ieee80211_ptr; @@ -1023,7 +1023,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len) mutex_unlock(&wil->mutex); return; } - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { if (wil->sta[evt->cid].status != wil_sta_unused) { @@ -1814,7 +1814,7 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len) wil->sta[cid].stats.ft_roams++; ether_addr_copy(wil->sta[cid].addr, vif->bss->bssid); mutex_unlock(&wil->mutex); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); cfg80211_ref_bss(wiphy, vif->bss); freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); @@ -4015,27 +4015,22 @@ int wmi_set_cqm_rssi_config(struct wil6210_priv *wil, struct wil6210_vif *vif = ndev_to_vif(ndev); int rc; struct { - struct wmi_set_link_monitor_cmd cmd; - s8 rssi_thold; - } __packed cmd = { - .cmd = { - .rssi_hyst = rssi_hyst, - .rssi_thresholds_list_size = 1, - }, - .rssi_thold = rssi_thold, - }; - struct { struct wmi_cmd_hdr hdr; struct wmi_set_link_monitor_event evt; } __packed reply = { .evt = {.status = WMI_FW_STATUS_FAILURE}, }; + DEFINE_FLEX(struct wmi_set_link_monitor_cmd, cmd, + rssi_thresholds_list, rssi_thresholds_list_size, 1); + + cmd->rssi_hyst = rssi_hyst; + cmd->rssi_thresholds_list[0] = rssi_thold; if (rssi_thold > S8_MAX || rssi_thold < S8_MIN || rssi_hyst > U8_MAX) return -EINVAL; - rc = wmi_call(wil, WMI_SET_LINK_MONITOR_CMDID, vif->mid, &cmd, - sizeof(cmd), WMI_SET_LINK_MONITOR_EVENTID, + rc = wmi_call(wil, WMI_SET_LINK_MONITOR_CMDID, vif->mid, cmd, + __struct_size(cmd), WMI_SET_LINK_MONITOR_EVENTID, &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); if (rc) { wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, rc %d\n", rc); diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 71bf2ae27a98..38f64524019e 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -474,7 +474,7 @@ struct wmi_start_scan_cmd { struct { u8 channel; u8 reserved; - } channel_list[]; + } channel_list[] __counted_by(num_channels); } __packed; #define WMI_MAX_PNO_SSID_NUM (16) @@ -3320,7 +3320,7 @@ struct wmi_set_link_monitor_cmd { u8 rssi_hyst; u8 reserved[12]; u8 rssi_thresholds_list_size; - s8 rssi_thresholds_list[]; + s8 rssi_thresholds_list[] __counted_by(rssi_thresholds_list_size); } __packed; /* wmi_link_monitor_event_type */ |