Merge branch 'wilc1000-move-out-of-staging'
authorKalle Valo <kvalo@codeaurora.org>
Thu, 2 Jul 2020 06:49:56 +0000 (09:49 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 2 Jul 2020 06:49:56 +0000 (09:49 +0300)
This is an immutable branch shared between wireless-drivers-next and
staging-next for moving wilc1000 driver out of staging to drivers/net/wireless
directory.

39 files changed:
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath11k/Kconfig
drivers/net/wireless/ath/ath11k/Makefile
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/dbring.c [new file with mode: 0644]
drivers/net/wireless/ath/ath11k/dbring.h [new file with mode: 0644]
drivers/net/wireless/ath/ath11k/debug.c
drivers/net/wireless/ath/ath11k/dp.c
drivers/net/wireless/ath/ath11k/dp.h
drivers/net/wireless/ath/ath11k/dp_rx.c
drivers/net/wireless/ath/ath11k/dp_tx.c
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/reg.c
drivers/net/wireless/ath/ath11k/spectral.c [new file with mode: 0644]
drivers/net/wireless/ath/ath11k/spectral.h [new file with mode: 0644]
drivers/net/wireless/ath/ath11k/wmi.c
drivers/net/wireless/ath/ath11k/wmi.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/carl9170/Kconfig
drivers/net/wireless/ath/carl9170/usb.c
drivers/net/wireless/ath/spectral_common.h
drivers/net/wireless/ath/wil6210/Kconfig
drivers/net/wireless/intel/iwlegacy/4965-mac.c
drivers/net/wireless/intel/iwlwifi/Kconfig
drivers/net/wireless/intel/iwlwifi/dvm/commands.h
drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
drivers/net/wireless/intel/iwlwifi/fw/dbg.c
drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
drivers/net/wireless/intel/iwlwifi/iwl-config.h
drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
drivers/net/wireless/intel/iwlwifi/iwl-trans.h
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/rs.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c

index 6e9d46b..d88edbf 100644 (file)
@@ -15,11 +15,11 @@ config WLAN_VENDOR_ATH
 
          For more information and documentation on this module you can visit:
 
-         http://wireless.kernel.org/en/users/Drivers/ath
+         https://wireless.wiki.kernel.org/en/users/Drivers/ath
 
          For information on all Atheros wireless drivers visit:
 
-         http://wireless.kernel.org/en/users/Drivers/Atheros
+         https://wireless.wiki.kernel.org/en/users/Drivers/Atheros
 
 if WLAN_VENDOR_ATH
 
index 4fd10ac..bbe8695 100644 (file)
@@ -1591,7 +1591,9 @@ static int ath10k_htt_tx_32(struct ath10k_htt *htt,
 err_unmap_msdu:
        dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
 err_free_msdu_id:
+       spin_lock_bh(&htt->tx_lock);
        ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+       spin_unlock_bh(&htt->tx_lock);
 err:
        return res;
 }
@@ -1798,7 +1800,9 @@ static int ath10k_htt_tx_64(struct ath10k_htt *htt,
 err_unmap_msdu:
        dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
 err_free_msdu_id:
+       spin_lock_bh(&htt->tx_lock);
        ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+       spin_unlock_bh(&htt->tx_lock);
 err:
        return res;
 }
index 7acb42e..88a9735 100644 (file)
@@ -34,3 +34,12 @@ config ATH11K_TRACING
        depends on ATH11K && EVENT_TRACING
        help
          Select this to use ath11k tracing infrastructure.
+
+config ATH11K_SPECTRAL
+       bool "QCA ath11k spectral scan support"
+       depends on ATH11K_DEBUGFS
+       depends on RELAY
+       help
+         Enable ath11k spectral scan support
+
+         Say Y to enable access to the FFT/spectral data via debugfs.
index fe7736e..1041863 100644 (file)
@@ -15,12 +15,14 @@ ath11k-y += core.o \
            dp_rx.o \
            debug.o \
            ce.o \
-           peer.o
+           peer.o \
+           dbring.o
 
 ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o debugfs_sta.o
 ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
 ath11k-$(CONFIG_ATH11K_TRACING) += trace.o
 ath11k-$(CONFIG_THERMAL) += thermal.o
+ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
index 02501cc..905cd8b 100644 (file)
@@ -400,8 +400,16 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab)
                goto err_dp_pdev_free;
        }
 
+       ret = ath11k_spectral_init(ab);
+       if (ret) {
+               ath11k_err(ab, "failed to init spectral %d\n", ret);
+               goto err_thermal_unregister;
+       }
+
        return 0;
 
+err_thermal_unregister:
+       ath11k_thermal_unregister(ab);
 err_dp_pdev_free:
        ath11k_dp_pdev_free(ab);
 err_mac_unregister:
@@ -414,6 +422,7 @@ err_pdev_debug:
 
 static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
 {
+       ath11k_spectral_deinit(ab);
        ath11k_thermal_unregister(ab);
        ath11k_mac_unregister(ab);
        ath11k_hif_irq_disable(ab);
@@ -582,6 +591,7 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab)
        ath11k_thermal_unregister(ab);
        ath11k_hif_irq_disable(ab);
        ath11k_dp_pdev_free(ab);
+       ath11k_spectral_deinit(ab);
        ath11k_hif_stop(ab);
        ath11k_wmi_detach(ab);
        ath11k_dp_pdev_reo_cleanup(ab);
index e04f0e7..e5c4e19 100644 (file)
@@ -21,6 +21,8 @@
 #include "hal_rx.h"
 #include "reg.h"
 #include "thermal.h"
+#include "dbring.h"
+#include "spectral.h"
 
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
 
@@ -215,12 +217,15 @@ struct ath11k_vif {
 
        bool is_started;
        bool is_up;
+       bool spectral_enabled;
        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 ath11k_vif_iter {
@@ -353,7 +358,10 @@ struct ath11k_sta {
 #endif
 };
 
-#define ATH11K_NUM_CHANS 41
+#define ATH11K_MIN_5G_FREQ 4150
+#define ATH11K_MIN_6G_FREQ 5945
+#define ATH11K_MAX_6G_FREQ 7115
+#define ATH11K_NUM_CHANS 100
 #define ATH11K_MAX_5G_CHAN 173
 
 enum ath11k_state {
@@ -431,6 +439,7 @@ struct ath11k {
        u32 vht_cap_info;
        struct ath11k_he ar_he;
        enum ath11k_state state;
+       bool supports_6ghz;
        struct {
                struct completion started;
                struct completion completed;
@@ -536,6 +545,9 @@ struct ath11k {
        u32 cached_ppdu_id;
 #ifdef CONFIG_ATH11K_DEBUGFS
        struct ath11k_debug debug;
+#endif
+#ifdef CONFIG_ATH11K_SPECTRAL
+       struct ath11k_spectral spectral;
 #endif
        bool dfs_block_radar_events;
        struct ath11k_thermal thermal;
@@ -548,6 +560,7 @@ struct ath11k_band_cap {
        u32 he_mcs;
        u32 he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE];
        struct ath11k_ppe_threshold he_ppet;
+       u16 he_6ghz_capa;
 };
 
 struct ath11k_pdev_cap {
@@ -579,12 +592,42 @@ struct ath11k_board_data {
 /* IPQ8074 HW channel counters frequency value in hertz */
 #define IPQ8074_CC_FREQ_HERTZ 320000
 
-struct ath11k_soc_dp_rx_stats {
+struct ath11k_bp_stats {
+       /* Head Pointer reported by the last HTT Backpressure event for the ring */
+       u16 hp;
+
+       /* Tail Pointer reported by the last HTT Backpressure event for the ring */
+       u16 tp;
+
+       /* Number of Backpressure events received for the ring */
+       u32 count;
+
+       /* Last recorded event timestamp */
+       unsigned long jiffies;
+};
+
+struct ath11k_dp_ring_bp_stats {
+       struct ath11k_bp_stats umac_ring_bp_stats[HTT_SW_UMAC_RING_IDX_MAX];
+       struct ath11k_bp_stats lmac_ring_bp_stats[HTT_SW_LMAC_RING_IDX_MAX][MAX_RADIOS];
+};
+
+struct ath11k_soc_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
+        * idr unavailable etc.
+        */
+       atomic_t misc_fail;
+};
+
+struct ath11k_soc_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 ath11k_soc_dp_tx_err_stats tx_err;
+       struct ath11k_dp_ring_bp_stats bp_stats;
 };
 
 /* Master structure to hold the hw data which may be used in core module */
@@ -653,7 +696,7 @@ struct ath11k_base {
        struct dentry *debugfs_soc;
        struct dentry *debugfs_ath11k;
 #endif
-       struct ath11k_soc_dp_rx_stats soc_stats;
+       struct ath11k_soc_dp_stats soc_stats;
 
        unsigned long dev_flags;
        struct completion driver_recovery;
@@ -668,6 +711,9 @@ struct ath11k_base {
        /* Round robbin based TCL ring selector */
        atomic_t tcl_ring_selector;
 
+       struct ath11k_dbring_cap *db_caps;
+       u32 num_db_cap;
+
        /* must be last */
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
diff --git a/drivers/net/wireless/ath/ath11k/dbring.c b/drivers/net/wireless/ath/ath11k/dbring.c
new file mode 100644 (file)
index 0000000..cf20db3
--- /dev/null
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#include "core.h"
+#include "debug.h"
+
+static int ath11k_dbring_bufs_replenish(struct ath11k *ar,
+                                       struct ath11k_dbring *ring,
+                                       struct ath11k_dbring_element *buff,
+                                       gfp_t gfp)
+{
+       struct ath11k_base *ab = ar->ab;
+       struct hal_srng *srng;
+       dma_addr_t paddr;
+       void *ptr_aligned, *ptr_unaligned, *desc;
+       int ret;
+       int buf_id;
+       u32 cookie;
+
+       srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
+
+       lockdep_assert_held(&srng->lock);
+
+       ath11k_hal_srng_access_begin(ab, srng);
+
+       ptr_unaligned = buff->payload;
+       ptr_aligned = PTR_ALIGN(ptr_unaligned, ring->buf_align);
+       paddr = dma_map_single(ab->dev, ptr_aligned, ring->buf_sz,
+                              DMA_FROM_DEVICE);
+
+       ret = dma_mapping_error(ab->dev, paddr);
+       if (ret)
+               goto err;
+
+       spin_lock_bh(&ring->idr_lock);
+       buf_id = idr_alloc(&ring->bufs_idr, buff, 0, ring->bufs_max, gfp);
+       spin_unlock_bh(&ring->idr_lock);
+       if (buf_id < 0) {
+               ret = -ENOBUFS;
+               goto err_dma_unmap;
+       }
+
+       desc = ath11k_hal_srng_src_get_next_entry(ab, srng);
+       if (!desc) {
+               ret = -ENOENT;
+               goto err_idr_remove;
+       }
+
+       buff->paddr = paddr;
+
+       cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, ar->pdev_idx) |
+                FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id);
+
+       ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, 0);
+
+       ath11k_hal_srng_access_end(ab, srng);
+
+       return 0;
+
+err_idr_remove:
+       spin_lock_bh(&ring->idr_lock);
+       idr_remove(&ring->bufs_idr, buf_id);
+       spin_unlock_bh(&ring->idr_lock);
+err_dma_unmap:
+       dma_unmap_single(ab->dev, paddr, ring->buf_sz,
+                        DMA_FROM_DEVICE);
+err:
+       ath11k_hal_srng_access_end(ab, srng);
+       return ret;
+}
+
+static int ath11k_dbring_fill_bufs(struct ath11k *ar,
+                                  struct ath11k_dbring *ring,
+                                  gfp_t gfp)
+{
+       struct ath11k_dbring_element *buff;
+       struct hal_srng *srng;
+       int num_remain, req_entries, num_free;
+       u32 align;
+       int size, ret;
+
+       srng = &ar->ab->hal.srng_list[ring->refill_srng.ring_id];
+
+       spin_lock_bh(&srng->lock);
+
+       num_free = ath11k_hal_srng_src_num_free(ar->ab, srng, true);
+       req_entries = min(num_free, ring->bufs_max);
+       num_remain = req_entries;
+       align = ring->buf_align;
+       size = sizeof(*buff) + ring->buf_sz + align - 1;
+
+       while (num_remain > 0) {
+               buff = kzalloc(size, gfp);
+               if (!buff)
+                       break;
+
+               ret = ath11k_dbring_bufs_replenish(ar, ring, buff, gfp);
+               if (ret) {
+                       ath11k_warn(ar->ab, "failed to replenish db ring num_remain %d req_ent %d\n",
+                                   num_remain, req_entries);
+                       kfree(buff);
+                       break;
+               }
+               num_remain--;
+       }
+
+       spin_unlock_bh(&srng->lock);
+
+       return num_remain;
+}
+
+int ath11k_dbring_wmi_cfg_setup(struct ath11k *ar,
+                               struct ath11k_dbring *ring,
+                               enum wmi_direct_buffer_module id)
+{
+       struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd param = {0};
+       int ret;
+
+       if (id >= WMI_DIRECT_BUF_MAX)
+               return -EINVAL;
+
+       param.pdev_id           = DP_SW2HW_MACID(ring->pdev_id);
+       param.module_id         = id;
+       param.base_paddr_lo     = lower_32_bits(ring->refill_srng.paddr);
+       param.base_paddr_hi     = upper_32_bits(ring->refill_srng.paddr);
+       param.head_idx_paddr_lo = lower_32_bits(ring->hp_addr);
+       param.head_idx_paddr_hi = upper_32_bits(ring->hp_addr);
+       param.tail_idx_paddr_lo = lower_32_bits(ring->tp_addr);
+       param.tail_idx_paddr_hi = upper_32_bits(ring->tp_addr);
+       param.num_elems         = ring->bufs_max;
+       param.buf_size          = ring->buf_sz;
+       param.num_resp_per_event = ring->num_resp_per_event;
+       param.event_timeout_ms  = ring->event_timeout_ms;
+
+       ret = ath11k_wmi_pdev_dma_ring_cfg(ar, &param);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath11k_dbring_set_cfg(struct ath11k *ar, struct ath11k_dbring *ring,
+                         u32 num_resp_per_event, u32 event_timeout_ms,
+                         int (*handler)(struct ath11k *,
+                                        struct ath11k_dbring_data *))
+{
+       if (WARN_ON(!ring))
+               return -EINVAL;
+
+       ring->num_resp_per_event = num_resp_per_event;
+       ring->event_timeout_ms = event_timeout_ms;
+       ring->handler = handler;
+
+       return 0;
+}
+
+int ath11k_dbring_buf_setup(struct ath11k *ar,
+                           struct ath11k_dbring *ring,
+                           struct ath11k_dbring_cap *db_cap)
+{
+       struct ath11k_base *ab = ar->ab;
+       struct hal_srng *srng;
+       int ret;
+
+       srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
+       ring->bufs_max = ring->refill_srng.size /
+                        ath11k_hal_srng_get_entrysize(HAL_RXDMA_DIR_BUF);
+
+       ring->buf_sz = db_cap->min_buf_sz;
+       ring->buf_align = db_cap->min_buf_align;
+       ring->pdev_id = db_cap->pdev_id;
+       ring->hp_addr = ath11k_hal_srng_get_hp_addr(ar->ab, srng);
+       ring->tp_addr = ath11k_hal_srng_get_tp_addr(ar->ab, srng);
+
+       ret = ath11k_dbring_fill_bufs(ar, ring, GFP_KERNEL);
+
+       return ret;
+}
+
+int ath11k_dbring_srng_setup(struct ath11k *ar, struct ath11k_dbring *ring,
+                            int ring_num, int num_entries)
+{
+       int ret;
+
+       ret = ath11k_dp_srng_setup(ar->ab, &ring->refill_srng, HAL_RXDMA_DIR_BUF,
+                                  ring_num, ar->pdev_idx, num_entries);
+       if (ret < 0) {
+               ath11k_warn(ar->ab, "failed to setup srng: %d ring_id %d\n",
+                           ret, ring_num);
+               goto err;
+       }
+
+       return 0;
+err:
+       ath11k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
+       return ret;
+}
+
+int ath11k_dbring_get_cap(struct ath11k_base *ab,
+                         u8 pdev_idx,
+                         enum wmi_direct_buffer_module id,
+                         struct ath11k_dbring_cap *db_cap)
+{
+       int i;
+
+       if (!ab->num_db_cap || !ab->db_caps)
+               return -ENOENT;
+
+       if (id >= WMI_DIRECT_BUF_MAX)
+               return -EINVAL;
+
+       for (i = 0; i < ab->num_db_cap; i++) {
+               if (pdev_idx == ab->db_caps[i].pdev_id &&
+                   id == ab->db_caps[i].id) {
+                       *db_cap = ab->db_caps[i];
+
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
+                                      struct ath11k_dbring_buf_release_event *ev)
+{
+       struct ath11k_dbring *ring;
+       struct hal_srng *srng;
+       struct ath11k *ar;
+       struct ath11k_dbring_element *buff;
+       struct ath11k_dbring_data handler_data;
+       struct ath11k_buffer_addr desc;
+       u8 *vaddr_unalign;
+       u32 num_entry, num_buff_reaped;
+       u8 pdev_idx, rbm;
+       u32 cookie;
+       int buf_id;
+       int size;
+       dma_addr_t paddr;
+       int ret = 0;
+
+       pdev_idx = ev->fixed.pdev_id;
+
+       if (pdev_idx >= ab->num_radios) {
+               ath11k_warn(ab, "Invalid pdev id %d\n", pdev_idx);
+               return -EINVAL;
+       }
+
+       if (ev->fixed.num_buf_release_entry !=
+           ev->fixed.num_meta_data_entry) {
+               ath11k_warn(ab, "Buffer entry %d mismatch meta entry %d\n",
+                           ev->fixed.num_buf_release_entry,
+                           ev->fixed.num_meta_data_entry);
+               return -EINVAL;
+       }
+
+       ar = ab->pdevs[pdev_idx].ar;
+
+       rcu_read_lock();
+       if (!rcu_dereference(ab->pdevs_active[pdev_idx])) {
+               ret = -EINVAL;
+               goto rcu_unlock;
+       }
+
+       switch (ev->fixed.module_id) {
+       case WMI_DIRECT_BUF_SPECTRAL:
+               ring = ath11k_spectral_get_dbring(ar);
+               break;
+       default:
+               ring = NULL;
+               ath11k_warn(ab, "Recv dma buffer release ev on unsupp module %d\n",
+                           ev->fixed.module_id);
+               break;
+       }
+
+       if (!ring) {
+               ret = -EINVAL;
+               goto rcu_unlock;
+       }
+
+       srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
+       num_entry = ev->fixed.num_buf_release_entry;
+       size = sizeof(*buff) + ring->buf_sz + ring->buf_align - 1;
+       num_buff_reaped = 0;
+
+       spin_lock_bh(&srng->lock);
+
+       while (num_buff_reaped < num_entry) {
+               desc.info0 = ev->buf_entry[num_buff_reaped].paddr_lo;
+               desc.info1 = ev->buf_entry[num_buff_reaped].paddr_hi;
+               handler_data.meta = ev->meta_data[num_buff_reaped];
+
+               num_buff_reaped++;
+
+               ath11k_hal_rx_buf_addr_info_get(&desc, &paddr, &cookie, &rbm);
+
+               buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie);
+
+               spin_lock_bh(&ring->idr_lock);
+               buff = idr_find(&ring->bufs_idr, buf_id);
+               if (!buff) {
+                       spin_unlock_bh(&ring->idr_lock);
+                       continue;
+               }
+               idr_remove(&ring->bufs_idr, buf_id);
+               spin_unlock_bh(&ring->idr_lock);
+
+               dma_unmap_single(ab->dev, buff->paddr, ring->buf_sz,
+                                DMA_FROM_DEVICE);
+
+               if (ring->handler) {
+                       vaddr_unalign = buff->payload;
+                       handler_data.data = PTR_ALIGN(vaddr_unalign,
+                                                     ring->buf_align);
+                       handler_data.data_sz = ring->buf_sz;
+
+                       ring->handler(ar, &handler_data);
+               }
+
+               memset(buff, 0, size);
+               ath11k_dbring_bufs_replenish(ar, ring, buff, GFP_ATOMIC);
+       }
+
+       spin_unlock_bh(&srng->lock);
+
+rcu_unlock:
+       rcu_read_unlock();
+
+       return ret;
+}
+
+void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring)
+{
+       ath11k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
+}
+
+void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring)
+{
+       struct ath11k_dbring_element *buff;
+       int buf_id;
+
+       spin_lock_bh(&ring->idr_lock);
+       idr_for_each_entry(&ring->bufs_idr, buff, buf_id) {
+               idr_remove(&ring->bufs_idr, buf_id);
+               dma_unmap_single(ar->ab->dev, buff->paddr,
+                                ring->buf_sz, DMA_FROM_DEVICE);
+               kfree(buff);
+       }
+
+       idr_destroy(&ring->bufs_idr);
+       spin_unlock_bh(&ring->idr_lock);
+}
diff --git a/drivers/net/wireless/ath/ath11k/dbring.h b/drivers/net/wireless/ath/ath11k/dbring.h
new file mode 100644 (file)
index 0000000..f7fce9e
--- /dev/null
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef ATH11K_DBRING_H
+#define ATH11K_DBRING_H
+
+#include <linux/types.h>
+#include <linux/idr.h>
+#include <linux/spinlock.h>
+#include "dp.h"
+
+struct ath11k_dbring_element {
+       dma_addr_t paddr;
+       u8 payload[0];
+};
+
+struct ath11k_dbring_data {
+       void *data;
+       u32 data_sz;
+       struct wmi_dma_buf_release_meta_data meta;
+};
+
+struct ath11k_dbring_buf_release_event {
+       struct ath11k_wmi_dma_buf_release_fixed_param fixed;
+       struct wmi_dma_buf_release_entry *buf_entry;
+       struct wmi_dma_buf_release_meta_data *meta_data;
+       u32 num_buf_entry;
+       u32 num_meta;
+};
+
+struct ath11k_dbring_cap {
+       u32 pdev_id;
+       enum wmi_direct_buffer_module id;
+       u32 min_elem;
+       u32 min_buf_sz;
+       u32 min_buf_align;
+};
+
+struct ath11k_dbring {
+       struct dp_srng refill_srng;
+       struct idr bufs_idr;
+       /* Protects bufs_idr */
+       spinlock_t idr_lock;
+       dma_addr_t tp_addr;
+       dma_addr_t hp_addr;
+       int bufs_max;
+       u32 pdev_id;
+       u32 buf_sz;
+       u32 buf_align;
+       u32 num_resp_per_event;
+       u32 event_timeout_ms;
+       int (*handler)(struct ath11k *, struct ath11k_dbring_data *);
+};
+
+int ath11k_dbring_set_cfg(struct ath11k *ar,
+                         struct ath11k_dbring *ring,
+                         u32 num_resp_per_event,
+                         u32 event_timeout_ms,
+                         int (*handler)(struct ath11k *,
+                                        struct ath11k_dbring_data *));
+int ath11k_dbring_wmi_cfg_setup(struct ath11k *ar,
+                               struct ath11k_dbring *ring,
+                               enum wmi_direct_buffer_module id);
+int ath11k_dbring_buf_setup(struct ath11k *ar,
+                           struct ath11k_dbring *ring,
+                           struct ath11k_dbring_cap *db_cap);
+int ath11k_dbring_srng_setup(struct ath11k *ar, struct ath11k_dbring *ring,
+                            int ring_num, int num_entries);
+int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
+                                      struct ath11k_dbring_buf_release_event *ev);
+int ath11k_dbring_get_cap(struct ath11k_base *ab,
+                         u8 pdev_idx,
+                         enum wmi_direct_buffer_module id,
+                         struct ath11k_dbring_cap *db_cap);
+void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring);
+void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring);
+#endif /* ATH11K_DBRING_H */
index 3fd6b5a..62a1aa0 100644 (file)
 #include "debug_htt_stats.h"
 #include "peer.h"
 
+static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
+       "REO2SW1_RING",
+       "REO2SW2_RING",
+       "REO2SW3_RING",
+       "REO2SW4_RING",
+       "WBM2REO_LINK_RING",
+       "REO2TCL_RING",
+       "REO2FW_RING",
+       "RELEASE_RING",
+       "PPE_RELEASE_RING",
+       "TCL2TQM_RING",
+       "TQM_RELEASE_RING",
+       "REO_RELEASE_RING",
+       "WBM2SW0_RELEASE_RING",
+       "WBM2SW1_RELEASE_RING",
+       "WBM2SW2_RELEASE_RING",
+       "WBM2SW3_RELEASE_RING",
+       "REO_CMD_RING",
+       "REO_STATUS_RING",
+};
+
+static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {
+       "FW2RXDMA_BUF_RING",
+       "FW2RXDMA_STATUS_RING",
+       "FW2RXDMA_LINK_RING",
+       "SW2RXDMA_BUF_RING",
+       "WBM2RXDMA_LINK_RING",
+       "RXDMA2FW_RING",
+       "RXDMA2SW_RING",
+       "RXDMA2RELEASE_RING",
+       "RXDMA2REO_RING",
+       "MONITOR_STATUS_RING",
+       "MONITOR_BUF_RING",
+       "MONITOR_DESC_RING",
+       "MONITOR_DEST_RING",
+};
+
 void ath11k_info(struct ath11k_base *ab, const char *fmt, ...)
 {
        struct va_format vaf = {
@@ -739,12 +776,78 @@ static const struct file_operations fops_extd_rx_stats = {
        .open = simple_open,
 };
 
-static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file,
+static int ath11k_fill_bp_stats(struct ath11k_base *ab,
+                               struct ath11k_bp_stats *bp_stats,
+                               char *buf, int len, int size)
+{
+       lockdep_assert_held(&ab->base_lock);
+
+       len += scnprintf(buf + len, size - len, "count: %u\n",
+                        bp_stats->count);
+       len += scnprintf(buf + len, size - len, "hp: %u\n",
+                        bp_stats->hp);
+       len += scnprintf(buf + len, size - len, "tp: %u\n",
+                        bp_stats->tp);
+       len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",
+                        jiffies_to_msecs(jiffies - bp_stats->jiffies));
+       return len;
+}
+
+static ssize_t ath11k_debug_dump_soc_ring_bp_stats(struct ath11k_base *ab,
+                                                  char *buf, int size)
+{
+       struct ath11k_bp_stats *bp_stats;
+       bool stats_rxd = false;
+       u8 i, pdev_idx;
+       int len = 0;
+
+       len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");
+       len += scnprintf(buf + len, size - len, "==================\n");
+
+       spin_lock_bh(&ab->base_lock);
+       for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {
+               bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];
+
+               if (!bp_stats->count)
+                       continue;
+
+               len += scnprintf(buf + len, size - len, "Ring: %s\n",
+                                htt_bp_umac_ring[i]);
+               len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
+               stats_rxd = true;
+       }
+
+       for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {
+               for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {
+                       bp_stats =
+                               &ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];
+
+                       if (!bp_stats->count)
+                               continue;
+
+                       len += scnprintf(buf + len, size - len, "Ring: %s\n",
+                                        htt_bp_lmac_ring[i]);
+                       len += scnprintf(buf + len, size - len, "pdev: %d\n",
+                                        pdev_idx);
+                       len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
+                       stats_rxd = true;
+               }
+       }
+       spin_unlock_bh(&ab->base_lock);
+
+       if (!stats_rxd)
+               len += scnprintf(buf + len, size - len,
+                                "No Ring Backpressure stats received\n\n");
+
+       return len;
+}
+
+static ssize_t ath11k_debug_dump_soc_dp_stats(struct file *file,
                                              char __user *user_buf,
                                              size_t count, loff_t *ppos)
 {
        struct ath11k_base *ab = file->private_data;
-       struct ath11k_soc_dp_rx_stats *soc_stats = &ab->soc_stats;
+       struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
        int len = 0, i, retval;
        const int size = 4096;
        static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {
@@ -788,6 +891,19 @@ static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file,
                         soc_stats->hal_reo_error[2],
                         soc_stats->hal_reo_error[3]);
 
+       len += scnprintf(buf + len, size - len, "\nSOC 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, soc_stats->tx_err.desc_na[i]);
+
+       len += scnprintf(buf + len, size - len,
+                        "\nMisc Transmit Failures: %d\n",
+                        atomic_read(&soc_stats->tx_err.misc_fail));
+
+       len += ath11k_debug_dump_soc_ring_bp_stats(ab, buf + len, size - len);
+
        if (len > size)
                len = size;
        retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
@@ -796,8 +912,8 @@ static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file,
        return retval;
 }
 
-static const struct file_operations fops_soc_rx_stats = {
-       .read = ath11k_debug_dump_soc_rx_stats,
+static const struct file_operations fops_soc_dp_stats = {
+       .read = ath11k_debug_dump_soc_dp_stats,
        .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
@@ -819,8 +935,8 @@ int ath11k_debug_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_rx_stats", 0600, ab->debugfs_soc, ab,
-                           &fops_soc_rx_stats);
+       debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
+                           &fops_soc_dp_stats);
 
        return 0;
 }
index 9ae743e..1d64c3c 100644 (file)
@@ -172,11 +172,12 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
        case HAL_RXDMA_DST:
        case HAL_RXDMA_MONITOR_DST:
        case HAL_RXDMA_MONITOR_DESC:
-       case HAL_RXDMA_DIR_BUF:
                params.intr_batch_cntr_thres_entries =
                                        HAL_SRNG_INT_BATCH_THRESHOLD_OTHER;
                params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER;
                break;
+       case HAL_RXDMA_DIR_BUF:
+               break;
        default:
                ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type);
                return -EINVAL;
index 058a5c1..7587862 100644 (file)
@@ -999,6 +999,48 @@ struct htt_resp_msg {
 #define HTT_BACKPRESSURE_EVENT_HP_M GENMASK(15, 0)
 #define HTT_BACKPRESSURE_EVENT_TP_M GENMASK(31, 16)
 
+#define HTT_BACKPRESSURE_UMAC_RING_TYPE        0
+#define HTT_BACKPRESSURE_LMAC_RING_TYPE        1
+
+enum htt_backpressure_umac_ringid {
+       HTT_SW_RING_IDX_REO_REO2SW1_RING,
+       HTT_SW_RING_IDX_REO_REO2SW2_RING,
+       HTT_SW_RING_IDX_REO_REO2SW3_RING,
+       HTT_SW_RING_IDX_REO_REO2SW4_RING,
+       HTT_SW_RING_IDX_REO_WBM2REO_LINK_RING,
+       HTT_SW_RING_IDX_REO_REO2TCL_RING,
+       HTT_SW_RING_IDX_REO_REO2FW_RING,
+       HTT_SW_RING_IDX_REO_REO_RELEASE_RING,
+       HTT_SW_RING_IDX_WBM_PPE_RELEASE_RING,
+       HTT_SW_RING_IDX_TCL_TCL2TQM_RING,
+       HTT_SW_RING_IDX_WBM_TQM_RELEASE_RING,
+       HTT_SW_RING_IDX_WBM_REO_RELEASE_RING,
+       HTT_SW_RING_IDX_WBM_WBM2SW0_RELEASE_RING,
+       HTT_SW_RING_IDX_WBM_WBM2SW1_RELEASE_RING,
+       HTT_SW_RING_IDX_WBM_WBM2SW2_RELEASE_RING,
+       HTT_SW_RING_IDX_WBM_WBM2SW3_RELEASE_RING,
+       HTT_SW_RING_IDX_REO_REO_CMD_RING,
+       HTT_SW_RING_IDX_REO_REO_STATUS_RING,
+       HTT_SW_UMAC_RING_IDX_MAX,
+};
+
+enum htt_backpressure_lmac_ringid {
+       HTT_SW_RING_IDX_FW2RXDMA_BUF_RING,
+       HTT_SW_RING_IDX_FW2RXDMA_STATUS_RING,
+       HTT_SW_RING_IDX_FW2RXDMA_LINK_RING,
+       HTT_SW_RING_IDX_SW2RXDMA_BUF_RING,
+       HTT_SW_RING_IDX_WBM2RXDMA_LINK_RING,
+       HTT_SW_RING_IDX_RXDMA2FW_RING,
+       HTT_SW_RING_IDX_RXDMA2SW_RING,
+       HTT_SW_RING_IDX_RXDMA2RELEASE_RING,
+       HTT_SW_RING_IDX_RXDMA2REO_RING,
+       HTT_SW_RING_IDX_MONITOR_STATUS_RING,
+       HTT_SW_RING_IDX_MONITOR_BUF_RING,
+       HTT_SW_RING_IDX_MONITOR_DESC_RING,
+       HTT_SW_RING_IDX_MONITOR_DEST_RING,
+       HTT_SW_LMAC_RING_IDX_MAX,
+};
+
 /* ppdu stats
  *
  * @details
index a54610d..791d971 100644 (file)
@@ -653,10 +653,8 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
        spin_lock_bh(&dp->reo_cmd_lock);
        list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list);
        dp->reo_cmd_cache_flush_count++;
-       spin_unlock_bh(&dp->reo_cmd_lock);
 
        /* Flush and invalidate aged REO desc from HW cache */
-       spin_lock_bh(&dp->reo_cmd_lock);
        list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list,
                                 list) {
                if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD ||
@@ -1503,9 +1501,10 @@ static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab,
                                                  struct sk_buff *skb)
 {
        u32 *data = (u32 *)skb->data;
-       u8 pdev_id, ring_type, ring_id;
+       u8 pdev_id, ring_type, ring_id, pdev_idx;
        u16 hp, tp;
        u32 backpressure_time;
+       struct ath11k_bp_stats *bp_stats;
 
        pdev_id = FIELD_GET(HTT_BACKPRESSURE_EVENT_PDEV_ID_M, *data);
        ring_type = FIELD_GET(HTT_BACKPRESSURE_EVENT_RING_TYPE_M, *data);
@@ -1520,6 +1519,31 @@ static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab,
 
        ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt backpressure event, pdev %d, ring type %d,ring id %d, hp %d tp %d, backpressure time %d\n",
                   pdev_id, ring_type, ring_id, hp, tp, backpressure_time);
+
+       if (ring_type == HTT_BACKPRESSURE_UMAC_RING_TYPE) {
+               if (ring_id >= HTT_SW_UMAC_RING_IDX_MAX)
+                       return;
+
+               bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[ring_id];
+       } else if (ring_type == HTT_BACKPRESSURE_LMAC_RING_TYPE) {
+               pdev_idx = DP_HW2SW_MACID(pdev_id);
+
+               if (ring_id >= HTT_SW_LMAC_RING_IDX_MAX || pdev_idx >= MAX_RADIOS)
+                       return;
+
+               bp_stats = &ab->soc_stats.bp_stats.lmac_ring_bp_stats[ring_id][pdev_idx];
+       } else {
+               ath11k_warn(ab, "unknown ring type received in htt bp event %d\n",
+                           ring_type);
+               return;
+       }
+
+       spin_lock_bh(&ab->base_lock);
+       bp_stats->hp = hp;
+       bp_stats->tp = tp;
+       bp_stats->count++;
+       bp_stats->jiffies = jiffies;
+       spin_unlock_bh(&ab->base_lock);
 }
 
 void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab,
@@ -2162,6 +2186,7 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
                                struct ieee80211_rx_status *rx_status)
 {
        u8 channel_num;
+       u32 center_freq;
 
        rx_status->freq = 0;
        rx_status->rate_idx = 0;
@@ -2172,8 +2197,11 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
        rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
 
        channel_num = ath11k_dp_rx_h_msdu_start_freq(rx_desc);
+       center_freq = ath11k_dp_rx_h_msdu_start_freq(rx_desc) >> 16;
 
-       if (channel_num >= 1 && channel_num <= 14) {
+       if (center_freq >= 5935 && center_freq <= 7105) {
+               rx_status->band = NL80211_BAND_6GHZ;
+       } else if (channel_num >= 1 && channel_num <= 14) {
                rx_status->band = NL80211_BAND_2GHZ;
        } else if (channel_num >= 36 && channel_num <= 173) {
                rx_status->band = NL80211_BAND_5GHZ;
index 41c990a..1af7677 100644 (file)
@@ -121,8 +121,10 @@ tcl_ring_sel:
        spin_unlock_bh(&tx_ring->tx_idr_lock);
 
        if (ret < 0) {
-               if (ring_map == (BIT(DP_TCL_NUM_RING_MAX) - 1))
+               if (ring_map == (BIT(DP_TCL_NUM_RING_MAX) - 1)) {
+                       atomic_inc(&ab->soc_stats.tx_err.misc_fail);
                        return -ENOSPC;
+               }
 
                /* Check if the next ring is available */
                ring_selector++;
@@ -180,11 +182,13 @@ tcl_ring_sel:
        default:
                /* TODO: Take care of other encap modes as well */
                ret = -EINVAL;
+               atomic_inc(&ab->soc_stats.tx_err.misc_fail);
                goto fail_remove_idr;
        }
 
        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);
                ath11k_warn(ab, "failed to DMA map data Tx buffer\n");
                ret = -ENOMEM;
                goto fail_remove_idr;
@@ -208,6 +212,7 @@ tcl_ring_sel:
                 * desc because the desc is directly enqueued onto hw queue.
                 */
                ath11k_hal_srng_access_end(ab, tcl_ring);
+               ab->soc_stats.tx_err.desc_na[ti.ring_id]++;
                spin_unlock_bh(&tcl_ring->lock);
                ret = -ENOMEM;
 
index 2836a0f..07d3e03 100644 (file)
        .max_power              = 30, \
 }
 
+#define CHAN6G(_channel, _freq, _flags) { \
+       .band                   = NL80211_BAND_6GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
 /* frame mode values are mapped as per enum ath11k_hw_txrx_mode */
 static unsigned int ath11k_frame_mode = ATH11K_HW_TXRX_NATIVE_WIFI;
 module_param_named(frame_mode, ath11k_frame_mode, uint, 0644);
@@ -86,6 +95,68 @@ static const struct ieee80211_channel ath11k_5ghz_channels[] = {
        CHAN5G(173, 5865, 0),
 };
 
+static const struct ieee80211_channel ath11k_6ghz_channels[] = {
+       CHAN6G(1, 5955, 0),
+       CHAN6G(5, 5975, 0),
+       CHAN6G(9, 5995, 0),
+       CHAN6G(13, 6015, 0),
+       CHAN6G(17, 6035, 0),
+       CHAN6G(21, 6055, 0),
+       CHAN6G(25, 6075, 0),
+       CHAN6G(29, 6095, 0),
+       CHAN6G(33, 6115, 0),
+       CHAN6G(37, 6135, 0),
+       CHAN6G(41, 6155, 0),
+       CHAN6G(45, 6175, 0),
+       CHAN6G(49, 6195, 0),
+       CHAN6G(53, 6215, 0),
+       CHAN6G(57, 6235, 0),
+       CHAN6G(61, 6255, 0),
+       CHAN6G(65, 6275, 0),
+       CHAN6G(69, 6295, 0),
+       CHAN6G(73, 6315, 0),
+       CHAN6G(77, 6335, 0),
+       CHAN6G(81, 6355, 0),
+       CHAN6G(85, 6375, 0),
+       CHAN6G(89, 6395, 0),
+       CHAN6G(93, 6415, 0),
+       CHAN6G(97, 6435, 0),
+       CHAN6G(101, 6455, 0),
+       CHAN6G(105, 6475, 0),
+       CHAN6G(109, 6495, 0),
+       CHAN6G(113, 6515, 0),
+       CHAN6G(117, 6535, 0),
+       CHAN6G(121, 6555, 0),
+       CHAN6G(125, 6575, 0),
+       CHAN6G(129, 6595, 0),
+       CHAN6G(133, 6615, 0),
+       CHAN6G(137, 6635, 0),
+       CHAN6G(141, 6655, 0),
+       CHAN6G(145, 6675, 0),
+       CHAN6G(149, 6695, 0),
+       CHAN6G(153, 6715, 0),
+       CHAN6G(157, 6735, 0),
+       CHAN6G(161, 6755, 0),
+       CHAN6G(165, 6775, 0),
+       CHAN6G(169, 6795, 0),
+       CHAN6G(173, 6815, 0),
+       CHAN6G(177, 6835, 0),
+       CHAN6G(181, 6855, 0),
+       CHAN6G(185, 6875, 0),
+       CHAN6G(189, 6895, 0),
+       CHAN6G(193, 6915, 0),
+       CHAN6G(197, 6935, 0),
+       CHAN6G(201, 6955, 0),
+       CHAN6G(205, 6975, 0),
+       CHAN6G(209, 6995, 0),
+       CHAN6G(213, 7015, 0),
+       CHAN6G(217, 7035, 0),
+       CHAN6G(221, 7055, 0),
+       CHAN6G(225, 7075, 0),
+       CHAN6G(229, 7095, 0),
+       CHAN6G(233, 7115, 0),
+};
+
 static struct ieee80211_rate ath11k_legacy_rates[] = {
        { .bitrate = 10,
          .hw_value = ATH11K_HW_RATE_CCK_LP_1M },
@@ -134,6 +205,17 @@ ath11k_phymodes[NUM_NL80211_BANDS][ATH11K_CHAN_WIDTH_NUM] = {
                        [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160,
                        [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80,
        },
+       [NL80211_BAND_6GHZ] = {
+                       [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
+                       [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
+                       [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20,
+                       [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20,
+                       [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40,
+                       [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80,
+                       [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160,
+                       [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80,
+       },
+
 };
 
 const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default = {
@@ -698,6 +780,8 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
        struct ieee80211_vif *vif = arvif->vif;
        struct ieee80211_mutable_offsets offs = {};
        struct sk_buff *bcn;
+       struct ieee80211_mgmt *mgmt;
+       u8 *ies;
        int ret;
 
        if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
@@ -709,6 +793,17 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
                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;
+
        ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn);
 
        kfree_skb(bcn);
@@ -798,6 +893,7 @@ static void ath11k_peer_assoc_h_crypto(struct ath11k *ar,
        struct ieee80211_bss_conf *info = &vif->bss_conf;
        struct cfg80211_chan_def def;
        struct cfg80211_bss *bss;
+       struct ath11k_vif *arvif = (struct ath11k_vif *)vif->drv_priv;
        const u8 *rsnie = NULL;
        const u8 *wpaie = NULL;
 
@@ -808,7 +904,12 @@ static void ath11k_peer_assoc_h_crypto(struct ath11k *ar,
 
        bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, NULL, 0,
                               IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
-       if (bss) {
+
+       if (arvif->rsnie_present || arvif->wpaie_present) {
+               arg->need_ptk_4_way = true;
+               if (arvif->wpaie_present)
+                       arg->need_gtk_2_way = true;
+       } else if (bss) {
                const struct cfg80211_bss_ies *ies;
 
                rcu_read_lock();
@@ -1489,6 +1590,7 @@ static void ath11k_peer_assoc_h_phymode(struct ath11k *ar,
                }
                break;
        case NL80211_BAND_5GHZ:
+       case NL80211_BAND_6GHZ:
                /* Check HE first */
                if (sta->he_cap.has_he) {
                        phymode = ath11k_mac_get_phymode_he(ar, sta);
@@ -2125,6 +2227,9 @@ static int ath11k_start_scan(struct ath11k *ar,
 
        lockdep_assert_held(&ar->conf_mutex);
 
+       if (ath11k_spectral_get_mode(ar) == ATH11K_SPECTRAL_BACKGROUND)
+               ath11k_spectral_reset_buffer(ar);
+
        ret = ath11k_wmi_send_scan_start_cmd(ar, arg);
        if (ret)
                return ret;
@@ -3411,7 +3516,7 @@ static void ath11k_mac_setup_ht_vht_cap(struct ath11k *ar,
                                                    rate_cap_rx_chainmask);
        }
 
-       if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) {
+       if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && !ar->supports_6ghz) {
                band = &ar->mac.sbands[NL80211_BAND_5GHZ];
                ht_cap = cap->band[NL80211_BAND_5GHZ].ht_cap_info;
                if (ht_cap_info)
@@ -3532,6 +3637,35 @@ ath11k_mac_filter_he_cap_mesh(struct ieee80211_he_cap_elem *he_cap_elem)
        he_cap_elem->phy_cap_info[9] &= ~m;
 }
 
+static __le16 ath11k_mac_setup_he_6ghz_cap(struct ath11k_pdev_cap *pcap,
+                                          struct ath11k_band_cap *bcap)
+{
+       u8 val;
+
+       bcap->he_6ghz_capa = IEEE80211_HT_MPDU_DENSITY_NONE;
+       if (bcap->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+               bcap->he_6ghz_capa |=
+                       FIELD_PREP(IEEE80211_HE_6GHZ_CAP_SM_PS,
+                                  WLAN_HT_CAP_SM_PS_DYNAMIC);
+       else
+               bcap->he_6ghz_capa |=
+                       FIELD_PREP(IEEE80211_HE_6GHZ_CAP_SM_PS,
+                                  WLAN_HT_CAP_SM_PS_DISABLED);
+       val = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+                       pcap->vht_cap);
+       bcap->he_6ghz_capa |=
+               FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP, val);
+       val = FIELD_GET(IEEE80211_VHT_CAP_MAX_MPDU_MASK, pcap->vht_cap);
+       bcap->he_6ghz_capa |=
+               FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN, val);
+       if (pcap->vht_cap & IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN)
+               bcap->he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
+       if (pcap->vht_cap & IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN)
+               bcap->he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS;
+
+       return cpu_to_le16(bcap->he_6ghz_capa);
+}
+
 static int ath11k_mac_copy_he_cap(struct ath11k *ar,
                                  struct ath11k_pdev_cap *cap,
                                  struct ieee80211_sband_iftype_data *data,
@@ -3614,6 +3748,11 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar,
                    IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT)
                        ath11k_gen_ppe_thresh(&band_cap->he_ppet,
                                              he_cap->ppe_thres);
+
+               if (band == NL80211_BAND_6GHZ) {
+                       data[idx].he_6ghz_capa.capa =
+                               ath11k_mac_setup_he_6ghz_cap(cap, band_cap);
+               }
                idx++;
        }
 
@@ -3643,6 +3782,16 @@ static void ath11k_mac_setup_he_cap(struct ath11k *ar,
                band->iftype_data = ar->mac.iftype[NL80211_BAND_5GHZ];
                band->n_iftype_data = count;
        }
+
+       if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP &&
+           ar->supports_6ghz) {
+               count = ath11k_mac_copy_he_cap(ar, cap,
+                                              ar->mac.iftype[NL80211_BAND_6GHZ],
+                                              NL80211_BAND_6GHZ);
+               band = &ar->mac.sbands[NL80211_BAND_6GHZ];
+               band->iftype_data = ar->mac.iftype[NL80211_BAND_6GHZ];
+               band->n_iftype_data = count;
+       }
 }
 
 static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant)
@@ -4085,6 +4234,11 @@ ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif,
                params->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains;
                params->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains;
        }
+       if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP &&
+           ar->supports_6ghz) {
+               params->chains[NL80211_BAND_6GHZ].tx = ar->num_tx_chains;
+               params->chains[NL80211_BAND_6GHZ].rx = ar->num_rx_chains;
+       }
 }
 
 static u32
@@ -5217,7 +5371,7 @@ ath11k_mac_get_single_legacy_rate(struct ath11k *ar,
 
        rate_idx = ffs(mask->control[band].legacy) - 1;
 
-       if (band == NL80211_BAND_5GHZ)
+       if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ)
                rate_idx += ATH11K_MAC_FIRST_OFDM_RATE_IDX;
 
        hw_rate = ath11k_legacy_rates[rate_idx].hw_value;
@@ -5683,7 +5837,8 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
        void *channels;
 
        BUILD_BUG_ON((ARRAY_SIZE(ath11k_2ghz_channels) +
-                     ARRAY_SIZE(ath11k_5ghz_channels)) !=
+                     ARRAY_SIZE(ath11k_5ghz_channels) +
+                     ARRAY_SIZE(ath11k_6ghz_channels)) !=
                     ATH11K_NUM_CHANS);
 
        reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx];
@@ -5696,6 +5851,7 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
                        return -ENOMEM;
 
                band = &ar->mac.sbands[NL80211_BAND_2GHZ];
+               band->band = NL80211_BAND_2GHZ;
                band->n_channels = ARRAY_SIZE(ath11k_2ghz_channels);
                band->channels = channels;
                band->n_bitrates = ath11k_g_rates_size;
@@ -5707,23 +5863,48 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
        }
 
        if (supported_bands & WMI_HOST_WLAN_5G_CAP) {
-               channels = kmemdup(ath11k_5ghz_channels,
-                                  sizeof(ath11k_5ghz_channels),
-                                  GFP_KERNEL);
-               if (!channels) {
-                       kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
-                       return -ENOMEM;
+               if (reg_cap->high_5ghz_chan >= ATH11K_MAX_6G_FREQ) {
+                       channels = kmemdup(ath11k_6ghz_channels,
+                                          sizeof(ath11k_6ghz_channels), GFP_KERNEL);
+                       if (!channels) {
+                               kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
+                               return -ENOMEM;
+                       }
+
+                       ar->supports_6ghz = true;
+                       band = &ar->mac.sbands[NL80211_BAND_6GHZ];
+                       band->band = NL80211_BAND_6GHZ;
+                       band->n_channels = ARRAY_SIZE(ath11k_6ghz_channels);
+                       band->channels = channels;
+                       band->n_bitrates = ath11k_a_rates_size;
+                       band->bitrates = ath11k_a_rates;
+                       ar->hw->wiphy->bands[NL80211_BAND_6GHZ] = band;
+                       ath11k_mac_update_ch_list(ar, band,
+                                                 reg_cap->low_5ghz_chan,
+                                                 reg_cap->high_5ghz_chan);
                }
 
-               band = &ar->mac.sbands[NL80211_BAND_5GHZ];
-               band->n_channels = ARRAY_SIZE(ath11k_5ghz_channels);
-               band->channels = channels;
-               band->n_bitrates = ath11k_a_rates_size;
-               band->bitrates = ath11k_a_rates;
-               ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
-               ath11k_mac_update_ch_list(ar, band,
-                                         reg_cap->low_5ghz_chan,
-                                         reg_cap->high_5ghz_chan);
+               if (reg_cap->low_5ghz_chan < ATH11K_MIN_6G_FREQ) {
+                       channels = kmemdup(ath11k_5ghz_channels,
+                                          sizeof(ath11k_5ghz_channels),
+                                          GFP_KERNEL);
+                       if (!channels) {
+                               kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
+                               kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels);
+                               return -ENOMEM;
+                       }
+
+                       band = &ar->mac.sbands[NL80211_BAND_5GHZ];
+                       band->band = NL80211_BAND_5GHZ;
+                       band->n_channels = ARRAY_SIZE(ath11k_5ghz_channels);
+                       band->channels = channels;
+                       band->n_bitrates = ath11k_a_rates_size;
+                       band->bitrates = ath11k_a_rates;
+                       ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
+                       ath11k_mac_update_ch_list(ar, band,
+                                                 reg_cap->low_5ghz_chan,
+                                                 reg_cap->high_5ghz_chan);
+               }
        }
 
        return 0;
@@ -5777,6 +5958,7 @@ static void __ath11k_mac_unregister(struct ath11k *ar)
 
        kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
        kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels);
+       kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels);
 
        SET_IEEE80211_DEV(ar->hw, NULL);
 }
index 453aa9c..7c9dc91 100644 (file)
@@ -161,6 +161,10 @@ int ath11k_reg_update_chan_list(struct ath11k *ar)
                        else
                                ch->phy_mode = MODE_11A;
 
+                       if (channel->band == NL80211_BAND_6GHZ &&
+                           cfg80211_channel_is_psc(channel))
+                               ch->psc_channel = true;
+
                        ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
                                   "mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
                                   i, params->nallchans,
diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c
new file mode 100644 (file)
index 0000000..1c5d65b
--- /dev/null
@@ -0,0 +1,1023 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/relay.h>
+#include "core.h"
+#include "debug.h"
+
+#define ATH11K_SPECTRAL_NUM_RESP_PER_EVENT     2
+#define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS       1
+
+#define ATH11K_SPECTRAL_DWORD_SIZE             4
+/* HW bug, expected BIN size is 2 bytes but HW report as 4 bytes */
+#define ATH11K_SPECTRAL_BIN_SIZE               4
+#define ATH11K_SPECTRAL_ATH11K_MIN_BINS                64
+#define ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS     32
+#define ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS     256
+
+#define ATH11K_SPECTRAL_SAMPLE_FFT_BIN_MASK    0xFF
+
+#define ATH11K_SPECTRAL_SCAN_COUNT_MAX         4095
+
+/* Max channel computed by sum of 2g and 5g band channels */
+#define ATH11K_SPECTRAL_TOTAL_CHANNEL          41
+#define ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL    70
+#define ATH11K_SPECTRAL_PER_SAMPLE_SIZE                (sizeof(struct fft_sample_ath11k) + \
+                                                ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS)
+#define ATH11K_SPECTRAL_TOTAL_SAMPLE           (ATH11K_SPECTRAL_TOTAL_CHANNEL * \
+                                                ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL)
+#define ATH11K_SPECTRAL_SUB_BUFF_SIZE          ATH11K_SPECTRAL_PER_SAMPLE_SIZE
+#define ATH11K_SPECTRAL_NUM_SUB_BUF            ATH11K_SPECTRAL_TOTAL_SAMPLE
+
+#define ATH11K_SPECTRAL_20MHZ                  20
+#define ATH11K_SPECTRAL_40MHZ                  40
+#define ATH11K_SPECTRAL_80MHZ                  80
+
+#define ATH11K_SPECTRAL_SIGNATURE              0xFA
+
+#define ATH11K_SPECTRAL_TAG_RADAR_SUMMARY      0x0
+#define ATH11K_SPECTRAL_TAG_RADAR_FFT          0x1
+#define ATH11K_SPECTRAL_TAG_SCAN_SUMMARY       0x2
+#define ATH11K_SPECTRAL_TAG_SCAN_SEARCH                0x3
+
+#define SPECTRAL_TLV_HDR_LEN                           GENMASK(15, 0)
+#define SPECTRAL_TLV_HDR_TAG                           GENMASK(23, 16)
+#define SPECTRAL_TLV_HDR_SIGN                          GENMASK(31, 24)
+
+#define SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN          GENMASK(7, 0)
+#define SPECTRAL_SUMMARY_INFO0_OB_FLAG                 BIT(8)
+#define SPECTRAL_SUMMARY_INFO0_GRP_IDX                 GENMASK(16, 9)
+#define SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT            BIT(17)
+#define SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB           GENMASK(27, 18)
+#define SPECTRAL_SUMMARY_INFO0_FALSE_SCAN              BIT(28)
+#define SPECTRAL_SUMMARY_INFO0_DETECTOR_ID             GENMASK(30, 29)
+#define SPECTRAL_SUMMARY_INFO0_PRI80                   BIT(31)
+
+#define SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX         GENMASK(11, 0)
+#define SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE          GENMASK(21, 12)
+#define SPECTRAL_SUMMARY_INFO2_NARROWBAND_MASK         GENMASK(29, 22)
+#define SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE             BIT(30)
+
+struct spectral_tlv {
+       __le32 timestamp;
+       __le32 header;
+} __packed;
+
+struct spectral_summary_fft_report {
+       __le32 timestamp;
+       __le32 tlv_header;
+       __le32 info0;
+       __le32 reserve0;
+       __le32 info2;
+       __le32 reserve1;
+} __packed;
+
+struct ath11k_spectral_summary_report {
+       struct wmi_dma_buf_release_meta_data meta;
+       u32 timestamp;
+       u8 agc_total_gain;
+       u8 grp_idx;
+       u16 inb_pwr_db;
+       s16 peak_idx;
+       u16 peak_mag;
+       u8 detector_id;
+       bool out_of_band_flag;
+       bool rf_saturation;
+       bool primary80;
+       bool gain_change;
+       bool false_scan;
+};
+
+#define SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID          GENMASK(1, 0)
+#define SPECTRAL_FFT_REPORT_INFO0_FFT_NUM              GENMASK(4, 2)
+#define SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK          GENMASK(16, 5)
+#define SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX      GENMASK(27, 17)
+#define SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX            GENMASK(30, 28)
+
+#define SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB          GENMASK(8, 0)
+#define SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB                GENMASK(16, 9)
+
+#define SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS      GENMASK(7, 0)
+#define SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE       GENMASK(17, 8)
+#define SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB           GENMASK(24, 18)
+#define SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB           GENMASK(31, 25)
+
+struct spectral_search_fft_report {
+       __le32 timestamp;
+       __le32 tlv_header;
+       __le32 info0;
+       __le32 info1;
+       __le32 info2;
+       __le32 reserve0;
+       u8 bins[0];
+} __packed;
+
+struct ath11k_spectral_search_report {
+       u32 timestamp;
+       u8 detector_id;
+       u8 fft_count;
+       u16 radar_check;
+       s16 peak_idx;
+       u8 chain_idx;
+       u16 base_pwr_db;
+       u8 total_gain_db;
+       u8 strong_bin_count;
+       u16 peak_mag;
+       u8 avg_pwr_db;
+       u8 rel_pwr_db;
+};
+
+static struct dentry *create_buf_file_handler(const char *filename,
+                                             struct dentry *parent,
+                                             umode_t mode,
+                                             struct rchan_buf *buf,
+                                             int *is_global)
+{
+       struct dentry *buf_file;
+
+       buf_file = debugfs_create_file(filename, mode, parent, buf,
+                                      &relay_file_operations);
+       *is_global = 1;
+       return buf_file;
+}
+
+static int remove_buf_file_handler(struct dentry *dentry)
+{
+       debugfs_remove(dentry);
+
+       return 0;
+}
+
+static struct rchan_callbacks rfs_scan_cb = {
+       .create_buf_file = create_buf_file_handler,
+       .remove_buf_file = remove_buf_file_handler,
+};
+
+static struct ath11k_vif *ath11k_spectral_get_vdev(struct ath11k *ar)
+{
+       struct ath11k_vif *arvif;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (list_empty(&ar->arvifs))
+               return NULL;
+
+       /* if there already is a vif doing spectral, return that. */
+       list_for_each_entry(arvif, &ar->arvifs, list)
+               if (arvif->spectral_enabled)
+                       return arvif;
+
+       /* otherwise, return the first vif. */
+       return list_first_entry(&ar->arvifs, typeof(*arvif), list);
+}
+
+static int ath11k_spectral_scan_trigger(struct ath11k *ar)
+{
+       struct ath11k_vif *arvif;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       arvif = ath11k_spectral_get_vdev(ar);
+       if (!arvif)
+               return -ENODEV;
+
+       if (ar->spectral.mode == ATH11K_SPECTRAL_DISABLED)
+               return 0;
+
+       ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
+                                             ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
+                                             ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
+       if (ret)
+               return ret;
+
+       ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
+                                             ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
+                                             ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ath11k_spectral_scan_config(struct ath11k *ar,
+                                      enum ath11k_spectral_mode mode)
+{
+       struct ath11k_wmi_vdev_spectral_conf_param param = { 0 };
+       struct ath11k_vif *arvif;
+       int ret, count;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       arvif = ath11k_spectral_get_vdev(ar);
+       if (!arvif)
+               return -ENODEV;
+
+       arvif->spectral_enabled = (mode != ATH11K_SPECTRAL_DISABLED);
+       ar->spectral.mode = mode;
+
+       ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
+                                             ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
+                                             ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to enable spectral scan: %d\n", ret);
+               return ret;
+       }
+
+       if (mode == ATH11K_SPECTRAL_DISABLED)
+               return 0;
+
+       if (mode == ATH11K_SPECTRAL_BACKGROUND)
+               count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
+       else
+               count = max_t(u16, 1, ar->spectral.count);
+
+       param.vdev_id = arvif->vdev_id;
+       param.scan_count = count;
+       param.scan_fft_size = ar->spectral.fft_size;
+       param.scan_period = ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT;
+       param.scan_priority = ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT;
+       param.scan_gc_ena = ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT;
+       param.scan_restart_ena = ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT;
+       param.scan_noise_floor_ref = ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
+       param.scan_init_delay = ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT;
+       param.scan_nb_tone_thr = ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
+       param.scan_str_bin_thr = ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
+       param.scan_wb_rpt_mode = ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
+       param.scan_rssi_rpt_mode = ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
+       param.scan_rssi_thr = ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT;
+       param.scan_pwr_format = ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
+       param.scan_rpt_mode = ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT;
+       param.scan_bin_scale = ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT;
+       param.scan_dbm_adj = ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT;
+       param.scan_chn_mask = ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT;
+
+       ret = ath11k_wmi_vdev_spectral_conf(ar, &param);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to configure spectral scan: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static ssize_t ath11k_read_file_spec_scan_ctl(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath11k *ar = file->private_data;
+       char *mode = "";
+       size_t len;
+       enum ath11k_spectral_mode spectral_mode;
+
+       mutex_lock(&ar->conf_mutex);
+       spectral_mode = ar->spectral.mode;
+       mutex_unlock(&ar->conf_mutex);
+
+       switch (spectral_mode) {
+       case ATH11K_SPECTRAL_DISABLED:
+               mode = "disable";
+               break;
+       case ATH11K_SPECTRAL_BACKGROUND:
+               mode = "background";
+               break;
+       case ATH11K_SPECTRAL_MANUAL:
+               mode = "manual";
+               break;
+       }
+
+       len = strlen(mode);
+       return simple_read_from_buffer(user_buf, count, ppos, mode, len);
+}
+
+static ssize_t ath11k_write_file_spec_scan_ctl(struct file *file,
+                                              const char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath11k *ar = file->private_data;
+       char buf[32];
+       ssize_t len;
+       int ret;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (strncmp("trigger", buf, 7) == 0) {
+               if (ar->spectral.mode == ATH11K_SPECTRAL_MANUAL ||
+                   ar->spectral.mode == ATH11K_SPECTRAL_BACKGROUND) {
+                       /* reset the configuration to adopt possibly changed
+                        * debugfs parameters
+                        */
+                       ret = ath11k_spectral_scan_config(ar, ar->spectral.mode);
+                       if (ret) {
+                               ath11k_warn(ar->ab, "failed to reconfigure spectral scan: %d\n",
+                                           ret);
+                               goto unlock;
+                       }
+
+                       ret = ath11k_spectral_scan_trigger(ar);
+                       if (ret) {
+                               ath11k_warn(ar->ab, "failed to trigger spectral scan: %d\n",
+                                           ret);
+                       }
+               } else {
+                       ret = -EINVAL;
+               }
+       } else if (strncmp("background", buf, 10) == 0) {
+               ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_BACKGROUND);
+       } else if (strncmp("manual", buf, 6) == 0) {
+               ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_MANUAL);
+       } else if (strncmp("disable", buf, 7) == 0) {
+               ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);
+       } else {
+               ret = -EINVAL;
+       }
+
+unlock:
+       mutex_unlock(&ar->conf_mutex);
+
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static const struct file_operations fops_scan_ctl = {
+       .read = ath11k_read_file_spec_scan_ctl,
+       .write = ath11k_write_file_spec_scan_ctl,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath11k_read_file_spectral_count(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath11k *ar = file->private_data;
+       char buf[32];
+       size_t len;
+       u16 spectral_count;
+
+       mutex_lock(&ar->conf_mutex);
+       spectral_count = ar->spectral.count;
+       mutex_unlock(&ar->conf_mutex);
+
+       len = sprintf(buf, "%d\n", spectral_count);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath11k_write_file_spectral_count(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct ath11k *ar = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val > ATH11K_SPECTRAL_SCAN_COUNT_MAX)
+               return -EINVAL;
+
+       mutex_lock(&ar->conf_mutex);
+       ar->spectral.count = val;
+       mutex_unlock(&ar->conf_mutex);
+
+       return count;
+}
+
+static const struct file_operations fops_scan_count = {
+       .read = ath11k_read_file_spectral_count,
+       .write = ath11k_write_file_spectral_count,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath11k_read_file_spectral_bins(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath11k *ar = file->private_data;
+       char buf[32];
+       unsigned int bins, fft_size;
+       size_t len;
+
+       mutex_lock(&ar->conf_mutex);
+
+       fft_size = ar->spectral.fft_size;
+       bins = 1 << fft_size;
+
+       mutex_unlock(&ar->conf_mutex);
+
+       len = sprintf(buf, "%d\n", bins);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath11k_write_file_spectral_bins(struct file *file,
+                                              const char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath11k *ar = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < ATH11K_SPECTRAL_ATH11K_MIN_BINS ||
+           val > SPECTRAL_ATH11K_MAX_NUM_BINS)
+               return -EINVAL;
+
+       if (!is_power_of_2(val))
+               return -EINVAL;
+
+       mutex_lock(&ar->conf_mutex);
+       ar->spectral.fft_size = ilog2(val);
+       mutex_unlock(&ar->conf_mutex);
+
+       return count;
+}
+
+static const struct file_operations fops_scan_bins = {
+       .read = ath11k_read_file_spectral_bins,
+       .write = ath11k_write_file_spectral_bins,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static int ath11k_spectral_pull_summary(struct ath11k *ar,
+                                       struct wmi_dma_buf_release_meta_data *meta,
+                                       struct spectral_summary_fft_report *summary,
+                                       struct ath11k_spectral_summary_report *report)
+{
+       report->timestamp = __le32_to_cpu(summary->timestamp);
+       report->agc_total_gain = FIELD_GET(SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN,
+                                          __le32_to_cpu(summary->info0));
+       report->out_of_band_flag = FIELD_GET(SPECTRAL_SUMMARY_INFO0_OB_FLAG,
+                                            __le32_to_cpu(summary->info0));
+       report->grp_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO0_GRP_IDX,
+                                   __le32_to_cpu(summary->info0));
+       report->rf_saturation = FIELD_GET(SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT,
+                                         __le32_to_cpu(summary->info0));
+       report->inb_pwr_db = FIELD_GET(SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB,
+                                      __le32_to_cpu(summary->info0));
+       report->false_scan = FIELD_GET(SPECTRAL_SUMMARY_INFO0_FALSE_SCAN,
+                                      __le32_to_cpu(summary->info0));
+       report->detector_id = FIELD_GET(SPECTRAL_SUMMARY_INFO0_DETECTOR_ID,
+                                       __le32_to_cpu(summary->info0));
+       report->primary80 = FIELD_GET(SPECTRAL_SUMMARY_INFO0_PRI80,
+                                     __le32_to_cpu(summary->info0));
+       report->peak_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX,
+                                    __le32_to_cpu(summary->info2));
+       report->peak_mag = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE,
+                                    __le32_to_cpu(summary->info2));
+       report->gain_change = FIELD_GET(SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE,
+                                       __le32_to_cpu(summary->info2));
+
+       memcpy(&report->meta, meta, sizeof(*meta));
+
+       return 0;
+}
+
+static int ath11k_spectral_pull_search(struct ath11k *ar,
+                                      struct spectral_search_fft_report *search,
+                                      struct ath11k_spectral_search_report *report)
+{
+       report->timestamp = __le32_to_cpu(search->timestamp);
+       report->detector_id = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID,
+                                       __le32_to_cpu(search->info0));
+       report->fft_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_FFT_NUM,
+                                     __le32_to_cpu(search->info0));
+       report->radar_check = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK,
+                                       __le32_to_cpu(search->info0));
+       report->peak_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
+                                    __le32_to_cpu(search->info0));
+       report->chain_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX,
+                                     __le32_to_cpu(search->info0));
+       report->base_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB,
+                                       __le32_to_cpu(search->info1));
+       report->total_gain_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB,
+                                         __le32_to_cpu(search->info1));
+       report->strong_bin_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS,
+                                            __le32_to_cpu(search->info2));
+       report->peak_mag = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE,
+                                    __le32_to_cpu(search->info2));
+       report->avg_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB,
+                                      __le32_to_cpu(search->info2));
+       report->rel_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB,
+                                      __le32_to_cpu(search->info2));
+
+       return 0;
+}
+
+static u8 ath11k_spectral_get_max_exp(s8 max_index, u8 max_magnitude,
+                                     int bin_len, u8 *bins)
+{
+       int dc_pos;
+       u8 max_exp;
+
+       dc_pos = bin_len / 2;
+
+       /* peak index outside of bins */
+       if (dc_pos <= max_index || -dc_pos >= max_index)
+               return 0;
+
+       for (max_exp = 0; max_exp < 8; max_exp++) {
+               if (bins[dc_pos + max_index] == (max_magnitude >> max_exp))
+                       break;
+       }
+
+       /* max_exp not found */
+       if (bins[dc_pos + max_index] != (max_magnitude >> max_exp))
+               return 0;
+
+       return max_exp;
+}
+
+static void ath11k_spectral_parse_16bit_fft(u8 *outbins, u8 *inbins, int num_bins)
+{
+       int i;
+       __le16 *data = (__le16 *)inbins;
+
+       i = 0;
+       while (i < num_bins) {
+               outbins[i] = (__le16_to_cpu(data[i])) &
+                            ATH11K_SPECTRAL_SAMPLE_FFT_BIN_MASK;
+               i++;
+       }
+}
+
+static
+int ath11k_spectral_process_fft(struct ath11k *ar,
+                               struct ath11k_spectral_summary_report *summary,
+                               void *data,
+                               struct fft_sample_ath11k *fft_sample,
+                               u32 data_len)
+{
+       struct ath11k_base *ab = ar->ab;
+       struct spectral_search_fft_report *fft_report = data;
+       struct ath11k_spectral_search_report search;
+       struct spectral_tlv *tlv;
+       int tlv_len, bin_len, num_bins;
+       u16 length, freq;
+       u8 chan_width_mhz;
+       int ret;
+
+       lockdep_assert_held(&ar->spectral.lock);
+
+       tlv = (struct spectral_tlv *)data;
+       tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN, __le32_to_cpu(tlv->header));
+       /* convert Dword into bytes */
+       tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
+       bin_len = tlv_len - (sizeof(*fft_report) - sizeof(*tlv));
+
+       if (data_len < (bin_len + sizeof(*fft_report))) {
+               ath11k_warn(ab, "mismatch in expected bin len %d and data len %d\n",
+                           bin_len, data_len);
+               return -EINVAL;
+       }
+
+       num_bins = bin_len / ATH11K_SPECTRAL_BIN_SIZE;
+       /* Only In-band bins are useful to user for visualize */
+       num_bins >>= 1;
+
+       if (num_bins < ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS ||
+           num_bins > ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS ||
+           !is_power_of_2(num_bins)) {
+               ath11k_warn(ab, "Invalid num of bins %d\n", num_bins);
+               return -EINVAL;
+       }
+
+       ret = ath11k_spectral_pull_search(ar, data, &search);
+       if (ret) {
+               ath11k_warn(ab, "failed to pull search report %d\n", ret);
+               return ret;
+       }
+
+       chan_width_mhz = summary->meta.ch_width;
+
+       switch (chan_width_mhz) {
+       case ATH11K_SPECTRAL_20MHZ:
+       case ATH11K_SPECTRAL_40MHZ:
+       case ATH11K_SPECTRAL_80MHZ:
+               fft_sample->chan_width_mhz = chan_width_mhz;
+               break;
+       default:
+               ath11k_warn(ab, "invalid channel width %d\n", chan_width_mhz);
+               return -EINVAL;
+       }
+
+       length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + num_bins;
+       fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH11K;
+       fft_sample->tlv.length = __cpu_to_be16(length);
+
+       fft_sample->tsf = __cpu_to_be32(search.timestamp);
+       fft_sample->max_magnitude = __cpu_to_be16(search.peak_mag);
+       fft_sample->max_index = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
+                                         __le32_to_cpu(fft_report->info0));
+
+       summary->inb_pwr_db >>= 1;
+       fft_sample->rssi = __cpu_to_be16(summary->inb_pwr_db);
+       fft_sample->noise = __cpu_to_be32(summary->meta.noise_floor[search.chain_idx]);
+
+       freq = summary->meta.freq1;
+       fft_sample->freq1 = __cpu_to_be16(freq);
+
+       freq = summary->meta.freq2;
+       fft_sample->freq2 = __cpu_to_be16(freq);
+
+       ath11k_spectral_parse_16bit_fft(fft_sample->data,
+                                       fft_report->bins,
+                                       num_bins);
+
+       fft_sample->max_exp = ath11k_spectral_get_max_exp(fft_sample->max_index,
+                                                         search.peak_mag,
+                                                         num_bins,
+                                                         fft_sample->data);
+
+       if (ar->spectral.rfs_scan)
+               relay_write(ar->spectral.rfs_scan, fft_sample,
+                           length + sizeof(struct fft_sample_tlv));
+
+       return 0;
+}
+
+static int ath11k_spectral_process_data(struct ath11k *ar,
+                                       struct ath11k_dbring_data *param)
+{
+       struct ath11k_base *ab = ar->ab;
+       struct spectral_tlv *tlv;
+       struct spectral_summary_fft_report *summary = NULL;
+       struct ath11k_spectral_summary_report summ_rpt;
+       struct fft_sample_ath11k *fft_sample = NULL;
+       u8 *data;
+       u32 data_len, i;
+       u8 sign, tag;
+       int tlv_len, sample_sz;
+       int ret;
+       bool quit = false;
+
+       spin_lock_bh(&ar->spectral.lock);
+
+       if (!ar->spectral.enabled) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS;
+       fft_sample = kmalloc(sample_sz, GFP_ATOMIC);
+       if (!fft_sample) {
+               ret = -ENOBUFS;
+               goto unlock;
+       }
+
+       data = param->data;
+       data_len = param->data_sz;
+       i = 0;
+       while (!quit && (i < data_len)) {
+               if ((i + sizeof(*tlv)) > data_len) {
+                       ath11k_warn(ab, "failed to parse spectral tlv hdr at bytes %d\n",
+                                   i);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               tlv = (struct spectral_tlv *)&data[i];
+               sign = FIELD_GET(SPECTRAL_TLV_HDR_SIGN,
+                                __le32_to_cpu(tlv->header));
+               if (sign != ATH11K_SPECTRAL_SIGNATURE) {
+                       ath11k_warn(ab, "Invalid sign 0x%x at bytes %d\n",
+                                   sign, i);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN,
+                                   __le32_to_cpu(tlv->header));
+               /* convert Dword into bytes */
+               tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
+               if ((i + sizeof(*tlv) + tlv_len) > data_len) {
+                       ath11k_warn(ab, "failed to parse spectral tlv payload at bytes %d tlv_len:%d data_len:%d\n",
+                                   i, tlv_len, data_len);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               tag = FIELD_GET(SPECTRAL_TLV_HDR_TAG,
+                               __le32_to_cpu(tlv->header));
+               switch (tag) {
+               case ATH11K_SPECTRAL_TAG_SCAN_SUMMARY:
+                       /* HW bug in tlv length of summary report,
+                        * HW report 3 DWORD size but the data payload
+                        * is 4 DWORD size (16 bytes).
+                        * Need to remove this workaround once HW bug fixed
+                        */
+                       tlv_len = sizeof(*summary) - sizeof(*tlv);
+
+                       if (tlv_len < (sizeof(*summary) - sizeof(*tlv))) {
+                               ath11k_warn(ab, "failed to parse spectral summary at bytes %d tlv_len:%d\n",
+                                           i, tlv_len);
+                               ret = -EINVAL;
+                               goto err;
+                       }
+
+                       summary = (struct spectral_summary_fft_report *)tlv;
+                       ath11k_spectral_pull_summary(ar, &param->meta,
+                                                    summary, &summ_rpt);
+                       break;
+               case ATH11K_SPECTRAL_TAG_SCAN_SEARCH:
+                       if (tlv_len < (sizeof(struct spectral_search_fft_report) -
+                                      sizeof(*tlv))) {
+                               ath11k_warn(ab, "failed to parse spectral search fft at bytes %d\n",
+                                           i);
+                               ret = -EINVAL;
+                               goto err;
+                       }
+
+                       memset(fft_sample, 0, sample_sz);
+                       ret = ath11k_spectral_process_fft(ar, &summ_rpt, tlv,
+                                                         fft_sample,
+                                                         data_len - i);
+                       if (ret) {
+                               ath11k_warn(ab, "failed to process spectral fft at bytes %d\n",
+                                           i);
+                               goto err;
+                       }
+                       quit = true;
+                       break;
+               }
+
+               i += sizeof(*tlv) + tlv_len;
+       }
+
+err:
+       kfree(fft_sample);
+unlock:
+       spin_unlock_bh(&ar->spectral.lock);
+       return ret;
+}
+
+static int ath11k_spectral_ring_alloc(struct ath11k *ar,
+                                     struct ath11k_dbring_cap *db_cap)
+{
+       struct ath11k_spectral *sp = &ar->spectral;
+       int ret;
+
+       ret = ath11k_dbring_srng_setup(ar, &sp->rx_ring,
+                                      0, db_cap->min_elem);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to setup db ring\n");
+               return ret;
+       }
+
+       ath11k_dbring_set_cfg(ar, &sp->rx_ring,
+                             ATH11K_SPECTRAL_NUM_RESP_PER_EVENT,
+                             ATH11K_SPECTRAL_EVENT_TIMEOUT_MS,
+                             ath11k_spectral_process_data);
+
+       ret = ath11k_dbring_buf_setup(ar, &sp->rx_ring, db_cap);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to setup db ring buffer\n");
+               goto srng_cleanup;
+       }
+
+       ret = ath11k_dbring_wmi_cfg_setup(ar, &sp->rx_ring,
+                                         WMI_DIRECT_BUF_SPECTRAL);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
+               goto buffer_cleanup;
+       }
+
+       return 0;
+
+buffer_cleanup:
+       ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
+srng_cleanup:
+       ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
+       return ret;
+}
+
+static inline void ath11k_spectral_ring_free(struct ath11k *ar)
+{
+       struct ath11k_spectral *sp = &ar->spectral;
+
+       if (!sp->enabled)
+               return;
+
+       ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
+       ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
+}
+
+static inline void ath11k_spectral_debug_unregister(struct ath11k *ar)
+{
+       debugfs_remove(ar->spectral.scan_bins);
+       ar->spectral.scan_bins = NULL;
+
+       debugfs_remove(ar->spectral.scan_count);
+       ar->spectral.scan_count = NULL;
+
+       debugfs_remove(ar->spectral.scan_ctl);
+       ar->spectral.scan_ctl = NULL;
+
+       if (ar->spectral.rfs_scan) {
+               relay_close(ar->spectral.rfs_scan);
+               ar->spectral.rfs_scan = NULL;
+       }
+}
+
+int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
+{
+       if (!arvif->spectral_enabled)
+               return 0;
+
+       return ath11k_spectral_scan_config(arvif->ar, ATH11K_SPECTRAL_DISABLED);
+}
+
+void ath11k_spectral_reset_buffer(struct ath11k *ar)
+{
+       if (!ar->spectral.enabled)
+               return;
+
+       if (ar->spectral.rfs_scan)
+               relay_reset(ar->spectral.rfs_scan);
+}
+
+void ath11k_spectral_deinit(struct ath11k_base *ab)
+{
+       struct ath11k *ar;
+       struct ath11k_spectral *sp;
+       int i;
+
+       for (i = 0; i <  ab->num_radios; i++) {
+               ar = ab->pdevs[i].ar;
+               sp = &ar->spectral;
+
+               if (!sp->enabled)
+                       continue;
+
+               ath11k_spectral_debug_unregister(ar);
+               ath11k_spectral_ring_free(ar);
+
+               spin_lock_bh(&sp->lock);
+
+               sp->mode = ATH11K_SPECTRAL_DISABLED;
+               sp->enabled = false;
+
+               spin_unlock_bh(&sp->lock);
+       }
+}
+
+static inline int ath11k_spectral_debug_register(struct ath11k *ar)
+{
+       int ret;
+
+       ar->spectral.rfs_scan = relay_open("spectral_scan",
+                                          ar->debug.debugfs_pdev,
+                                          ATH11K_SPECTRAL_SUB_BUFF_SIZE,
+                                          ATH11K_SPECTRAL_NUM_SUB_BUF,
+                                          &rfs_scan_cb, NULL);
+       if (!ar->spectral.rfs_scan) {
+               ath11k_warn(ar->ab, "failed to open relay in pdev %d\n",
+                           ar->pdev_idx);
+               return -EINVAL;
+       }
+
+       ar->spectral.scan_ctl = debugfs_create_file("spectral_scan_ctl",
+                                                   0600,
+                                                   ar->debug.debugfs_pdev, ar,
+                                                   &fops_scan_ctl);
+       if (!ar->spectral.scan_ctl) {
+               ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
+                           ar->pdev_idx);
+               ret = -EINVAL;
+               goto debug_unregister;
+       }
+
+       ar->spectral.scan_count = debugfs_create_file("spectral_count",
+                                                     0600,
+                                                     ar->debug.debugfs_pdev, ar,
+                                                     &fops_scan_count);
+       if (!ar->spectral.scan_count) {
+               ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
+                           ar->pdev_idx);
+               ret = -EINVAL;
+               goto debug_unregister;
+       }
+
+       ar->spectral.scan_bins = debugfs_create_file("spectral_bins",
+                                                    0600,
+                                                    ar->debug.debugfs_pdev, ar,
+                                                    &fops_scan_bins);
+       if (!ar->spectral.scan_bins) {
+               ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
+                           ar->pdev_idx);
+               ret = -EINVAL;
+               goto debug_unregister;
+       }
+
+       return 0;
+
+debug_unregister:
+       ath11k_spectral_debug_unregister(ar);
+       return ret;
+}
+
+int ath11k_spectral_init(struct ath11k_base *ab)
+{
+       struct ath11k *ar;
+       struct ath11k_spectral *sp;
+       struct ath11k_dbring_cap db_cap;
+       int ret;
+       int i;
+
+       if (!test_bit(WMI_TLV_SERVICE_FREQINFO_IN_METADATA,
+                     ab->wmi_ab.svc_map)) {
+               ath11k_info(ab, "spectral not supported\n");
+               return 0;
+       }
+
+       for (i = 0; i < ab->num_radios; i++) {
+               ar = ab->pdevs[i].ar;
+               sp = &ar->spectral;
+
+               ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,
+                                           WMI_DIRECT_BUF_SPECTRAL,
+                                           &db_cap);
+               if (ret) {
+                       ath11k_info(ab, "spectral not enabled for pdev %d\n", i);
+                       continue;
+               }
+
+               idr_init(&sp->rx_ring.bufs_idr);
+               spin_lock_init(&sp->rx_ring.idr_lock);
+               spin_lock_init(&sp->lock);
+
+               ret = ath11k_spectral_ring_alloc(ar, &db_cap);
+               if (ret) {
+                       ath11k_warn(ab, "failed to init spectral ring for pdev %d\n",
+                                   i);
+                       goto deinit;
+               }
+
+               spin_lock_bh(&sp->lock);
+
+               sp->mode = ATH11K_SPECTRAL_DISABLED;
+               sp->count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
+               sp->fft_size = ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT;
+               sp->enabled = true;
+
+               spin_unlock_bh(&sp->lock);
+
+               ret = ath11k_spectral_debug_register(ar);
+               if (ret) {
+                       ath11k_warn(ab, "failed to register spectral for pdev %d\n",
+                                   i);
+                       goto deinit;
+               }
+       }
+
+       return 0;
+
+deinit:
+       ath11k_spectral_deinit(ab);
+       return ret;
+}
+
+enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
+{
+       if (ar->spectral.enabled)
+               return ar->spectral.mode;
+       else
+               return ATH11K_SPECTRAL_DISABLED;
+}
+
+struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
+{
+       if (ar->spectral.enabled)
+               return &ar->spectral.rx_ring;
+       else
+               return NULL;
+}
diff --git a/drivers/net/wireless/ath/ath11k/spectral.h b/drivers/net/wireless/ath/ath11k/spectral.h
new file mode 100644 (file)
index 0000000..0817442
--- /dev/null
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef ATH11K_SPECTRAL_H
+#define ATH11K_SPECTRAL_H
+
+#include "../spectral_common.h"
+#include "dbring.h"
+
+/* enum ath11k_spectral_mode:
+ *
+ * @SPECTRAL_DISABLED: spectral mode is disabled
+ * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
+ *     something else.
+ * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
+ *     is performed manually.
+ */
+enum ath11k_spectral_mode {
+       ATH11K_SPECTRAL_DISABLED = 0,
+       ATH11K_SPECTRAL_BACKGROUND,
+       ATH11K_SPECTRAL_MANUAL,
+};
+
+struct ath11k_spectral {
+       struct ath11k_dbring rx_ring;
+       /* Protects enabled */
+       spinlock_t lock;
+       struct rchan *rfs_scan; /* relay(fs) channel for spectral scan */
+       struct dentry *scan_ctl;
+       struct dentry *scan_count;
+       struct dentry *scan_bins;
+       enum ath11k_spectral_mode mode;
+       u16 count;
+       u8 fft_size;
+       bool enabled;
+};
+
+#ifdef CONFIG_ATH11K_SPECTRAL
+
+int ath11k_spectral_init(struct ath11k_base *ab);
+void ath11k_spectral_deinit(struct ath11k_base *ab);
+int ath11k_spectral_vif_stop(struct ath11k_vif *arvif);
+void ath11k_spectral_reset_buffer(struct ath11k *ar);
+enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar);
+struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar);
+
+#else
+
+static inline int ath11k_spectral_init(struct ath11k_base *ab)
+{
+       return 0;
+}
+
+static inline void ath11k_spectral_deinit(struct ath11k_base *ab)
+{
+}
+
+static inline int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
+{
+       return 0;
+}
+
+static inline void ath11k_spectral_reset_buffer(struct ath11k *ar)
+{
+}
+
+static inline
+enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
+{
+       return ATH11K_SPECTRAL_DISABLED;
+}
+
+static inline
+struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
+{
+       return NULL;
+}
+
+#endif /* CONFIG_ATH11K_SPECTRAL */
+#endif /* ATH11K_SPECTRAL_H */
index c2a9723..8e3437a 100644 (file)
@@ -27,6 +27,11 @@ struct wmi_tlv_svc_ready_parse {
        bool wmi_svc_bitmap_done;
 };
 
+struct wmi_tlv_dma_ring_caps_parse {
+       struct wmi_dma_ring_capabilities *dma_ring_caps;
+       u32 n_dma_ring_caps;
+};
+
 struct wmi_tlv_svc_rdy_ext_parse {
        struct ath11k_service_ext_param param;
        struct wmi_soc_mac_phy_hw_mode_caps *hw_caps;
@@ -39,15 +44,35 @@ struct wmi_tlv_svc_rdy_ext_parse {
        struct wmi_soc_hal_reg_capabilities *soc_hal_reg_caps;
        struct wmi_hal_reg_capabilities_ext *ext_hal_reg_caps;
        u32 n_ext_hal_reg_caps;
+       struct wmi_tlv_dma_ring_caps_parse dma_caps_parse;
        bool hw_mode_done;
        bool mac_phy_done;
        bool ext_hal_reg_done;
+       bool mac_phy_chainmask_combo_done;
+       bool mac_phy_chainmask_cap_done;
+       bool oem_dma_ring_cap_done;
+       bool dma_ring_cap_done;
+};
+
+struct wmi_tlv_svc_rdy_ext2_parse {
+       struct wmi_tlv_dma_ring_caps_parse dma_caps_parse;
+       bool dma_ring_cap_done;
 };
 
 struct wmi_tlv_rdy_parse {
        u32 num_extra_mac_addr;
 };
 
+struct wmi_tlv_dma_buf_release_parse {
+       struct ath11k_wmi_dma_buf_release_fixed_param fixed;
+       struct wmi_dma_buf_release_entry *buf_entry;
+       struct wmi_dma_buf_release_meta_data *meta_data;
+       u32 num_buf_entry;
+       u32 num_meta;
+       bool buf_entry_done;
+       bool meta_data_done;
+};
+
 static const struct wmi_tlv_policy wmi_tlv_policies[] = {
        [WMI_TAG_ARRAY_BYTE]
                = { .min_len = 0 },
@@ -368,6 +393,17 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle,
        memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g,
               sizeof(struct ath11k_ppe_threshold));
 
+       cap_band = &pdev_cap->band[NL80211_BAND_6GHZ];
+       cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g;
+       cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g;
+       cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g;
+       cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext;
+       cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g;
+       memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g,
+              sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE);
+       memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g,
+              sizeof(struct ath11k_ppe_threshold));
+
        return 0;
 }
 
@@ -1692,10 +1728,10 @@ ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,
         */
        if (param->auth_flag)
                cmd->peer_flags |= WMI_PEER_AUTH;
-       if (param->need_ptk_4_way)
+       if (param->need_ptk_4_way) {
                cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
-       else
-               cmd->peer_flags &= ~WMI_PEER_NEED_PTK_4_WAY;
+               cmd->peer_flags &= ~WMI_PEER_AUTH;
+       }
        if (param->need_gtk_2_way)
                cmd->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
        /* safe mode bypass the 4-way handshake */
@@ -1778,6 +1814,7 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
        cmd->peer_he_cap_info = param->peer_he_cap_macinfo[0];
        cmd->peer_he_cap_info_ext = param->peer_he_cap_macinfo[1];
        cmd->peer_he_cap_info_internal = param->peer_he_cap_macinfo_internal;
+       cmd->peer_he_caps_6ghz = param->peer_he_caps_6ghz;
        cmd->peer_he_ops = param->peer_he_ops;
        memcpy(&cmd->peer_he_cap_phy, &param->peer_he_cap_phyinfo,
               sizeof(param->peer_he_cap_phyinfo));
@@ -1831,6 +1868,7 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
 
        /* HE Rates */
        cmd->peer_he_mcs = param->peer_he_mcs_count;
+       cmd->min_data_rate = param->min_data_rate;
 
        ptr += sizeof(*mcs);
 
@@ -1886,6 +1924,8 @@ void ath11k_wmi_start_scan_init(struct ath11k *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->min_rest_time = 50;
        arg->max_rest_time = 500;
        arg->repeat_probe_time = 0;
@@ -1990,6 +2030,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
        int i, ret, len;
        u32 *tmp_ptr;
        u8 extraie_len_with_pad = 0;
+       struct hint_short_ssid *s_ssid = NULL;
+       struct hint_bssid *hint_bssid = NULL;
 
        len = sizeof(*cmd);
 
@@ -2011,6 +2053,14 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
                        roundup(params->extraie.len, sizeof(u32));
        len += extraie_len_with_pad;
 
+       if (params->num_hint_bssid)
+               len += TLV_HDR_SIZE +
+                      params->num_hint_bssid * sizeof(struct hint_bssid);
+
+       if (params->num_hint_s_ssid)
+               len += TLV_HDR_SIZE +
+                      params->num_hint_s_ssid * sizeof(struct hint_short_ssid);
+
        skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
        if (!skb)
                return -ENOMEM;
@@ -2032,6 +2082,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
        cmd->dwell_time_active = params->dwell_time_active;
        cmd->dwell_time_active_2g = params->dwell_time_active_2g;
        cmd->dwell_time_passive = params->dwell_time_passive;
+       cmd->dwell_time_active_6g = params->dwell_time_active_6g;
+       cmd->dwell_time_passive_6g = params->dwell_time_passive_6g;
        cmd->min_rest_time = params->min_rest_time;
        cmd->max_rest_time = params->max_rest_time;
        cmd->repeat_probe_time = params->repeat_probe_time;
@@ -2109,6 +2161,68 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
 
        ptr += extraie_len_with_pad;
 
+       if (params->num_hint_s_ssid) {
+               len = params->num_hint_s_ssid * sizeof(struct hint_short_ssid);
+               tlv = ptr;
+               tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+                             FIELD_PREP(WMI_TLV_LEN, len);
+               ptr += TLV_HDR_SIZE;
+               s_ssid = ptr;
+               for (i = 0; i < params->num_hint_s_ssid; ++i) {
+                       s_ssid->freq_flags = params->hint_s_ssid[i].freq_flags;
+                       s_ssid->short_ssid = params->hint_s_ssid[i].short_ssid;
+                       s_ssid++;
+               }
+               ptr += len;
+       }
+
+       if (params->num_hint_bssid) {
+               len = params->num_hint_bssid * sizeof(struct hint_bssid);
+               tlv = ptr;
+               tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+                             FIELD_PREP(WMI_TLV_LEN, len);
+               ptr += TLV_HDR_SIZE;
+               hint_bssid = ptr;
+               for (i = 0; i < params->num_hint_bssid; ++i) {
+                       hint_bssid->freq_flags =
+                               params->hint_bssid[i].freq_flags;
+                       ether_addr_copy(&params->hint_bssid[i].bssid.addr[0],
+                                       &hint_bssid->bssid.addr[0]);
+                       hint_bssid++;
+               }
+       }
+
+       len = params->num_hint_s_ssid * sizeof(struct hint_short_ssid);
+       tlv = ptr;
+       tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+                     FIELD_PREP(WMI_TLV_LEN, len);
+       ptr += TLV_HDR_SIZE;
+       if (params->num_hint_s_ssid) {
+               s_ssid = ptr;
+               for (i = 0; i < params->num_hint_s_ssid; ++i) {
+                       s_ssid->freq_flags = params->hint_s_ssid[i].freq_flags;
+                       s_ssid->short_ssid = params->hint_s_ssid[i].short_ssid;
+                       s_ssid++;
+               }
+       }
+       ptr += len;
+
+       len = params->num_hint_bssid * sizeof(struct hint_bssid);
+       tlv = ptr;
+       tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+                     FIELD_PREP(WMI_TLV_LEN, len);
+       ptr += TLV_HDR_SIZE;
+       if (params->num_hint_bssid) {
+               hint_bssid = ptr;
+               for (i = 0; i < params->num_hint_bssid; ++i) {
+                       hint_bssid->freq_flags =
+                               params->hint_bssid[i].freq_flags;
+                       ether_addr_copy(&params->hint_bssid[i].bssid.addr[0],
+                                       &hint_bssid->bssid.addr[0]);
+                       hint_bssid++;
+               }
+       }
+
        ret = ath11k_wmi_cmd_send(wmi, skb,
                                  WMI_START_SCAN_CMDID);
        if (ret) {
@@ -2178,91 +2292,110 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar,
        struct wmi_tlv *tlv;
        void *ptr;
        int i, ret, len;
+       u16 num_send_chans, num_sends = 0, max_chan_limit = 0;
        u32 *reg1, *reg2;
 
-       len = sizeof(*cmd) + TLV_HDR_SIZE +
-                sizeof(*chan_info) * chan_list->nallchans;
+       tchan_info = &chan_list->ch_param[0];
+       while (chan_list->nallchans) {
+               len = sizeof(*cmd) + TLV_HDR_SIZE;
+               max_chan_limit = (wmi->wmi_ab->max_msg_len[ar->pdev_idx] - len) /
+                       sizeof(*chan_info);
 
-       skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
-       if (!skb)
-               return -ENOMEM;
+               if (chan_list->nallchans > max_chan_limit)
+                       num_send_chans = max_chan_limit;
+               else
+                       num_send_chans = chan_list->nallchans;
 
-       cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
-       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SCAN_CHAN_LIST_CMD) |
-                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+               chan_list->nallchans -= num_send_chans;
+               len += sizeof(*chan_info) * num_send_chans;
 
-       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
-                  "WMI no.of chan = %d len = %d\n", chan_list->nallchans, len);
-       cmd->pdev_id = chan_list->pdev_id;
-       cmd->num_scan_chans = chan_list->nallchans;
-
-       ptr = skb->data + sizeof(*cmd);
+               skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+               if (!skb)
+                       return -ENOMEM;
 
-       len = sizeof(*chan_info) * chan_list->nallchans;
-       tlv = ptr;
-       tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
-                     FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
-       ptr += TLV_HDR_SIZE;
+               cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
+               cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SCAN_CHAN_LIST_CMD) |
+                       FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+               cmd->pdev_id = chan_list->pdev_id;
+               cmd->num_scan_chans = num_send_chans;
+               if (num_sends)
+                       cmd->flags |= WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG;
 
-       tchan_info = &chan_list->ch_param[0];
+               ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                          "WMI no.of chan = %d len = %d pdev_id = %d num_sends = %d\n",
+                          num_send_chans, len, cmd->pdev_id, num_sends);
 
-       for (i = 0; i < chan_list->nallchans; ++i) {
-               chan_info = ptr;
-               memset(chan_info, 0, sizeof(*chan_info));
-               len = sizeof(*chan_info);
-               chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG,
-                                                  WMI_TAG_CHANNEL) |
-                                       FIELD_PREP(WMI_TLV_LEN,
-                                                  len - TLV_HDR_SIZE);
-
-               reg1 = &chan_info->reg_info_1;
-               reg2 = &chan_info->reg_info_2;
-               chan_info->mhz = tchan_info->mhz;
-               chan_info->band_center_freq1 = tchan_info->cfreq1;
-               chan_info->band_center_freq2 = tchan_info->cfreq2;
-
-               if (tchan_info->is_chan_passive)
-                       chan_info->info |= WMI_CHAN_INFO_PASSIVE;
-               if (tchan_info->allow_he)
-                       chan_info->info |= WMI_CHAN_INFO_ALLOW_HE;
-               else if (tchan_info->allow_vht)
-                       chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT;
-               else if (tchan_info->allow_ht)
-                       chan_info->info |= WMI_CHAN_INFO_ALLOW_HT;
-               if (tchan_info->half_rate)
-                       chan_info->info |= WMI_CHAN_INFO_HALF_RATE;
-               if (tchan_info->quarter_rate)
-                       chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE;
-
-               chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE,
-                                             tchan_info->phy_mode);
-               *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR,
-                                   tchan_info->minpower);
-               *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR,
-                                   tchan_info->maxpower);
-               *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR,
-                                   tchan_info->maxregpower);
-               *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS,
-                                   tchan_info->reg_class_id);
-               *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX,
-                                   tchan_info->antennamax);
+               ptr = skb->data + sizeof(*cmd);
 
-               ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
-                          "WMI chan scan list chan[%d] = %u\n",
-                          i, chan_info->mhz);
+               len = sizeof(*chan_info) * num_send_chans;
+               tlv = ptr;
+               tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
+                             FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
+               ptr += TLV_HDR_SIZE;
 
-               ptr += sizeof(*chan_info);
+               for (i = 0; i < num_send_chans; ++i) {
+                       chan_info = ptr;
+                       memset(chan_info, 0, sizeof(*chan_info));
+                       len = sizeof(*chan_info);
+                       chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                                          WMI_TAG_CHANNEL) |
+                                               FIELD_PREP(WMI_TLV_LEN,
+                                                          len - TLV_HDR_SIZE);
+
+                       reg1 = &chan_info->reg_info_1;
+                       reg2 = &chan_info->reg_info_2;
+                       chan_info->mhz = tchan_info->mhz;
+                       chan_info->band_center_freq1 = tchan_info->cfreq1;
+                       chan_info->band_center_freq2 = tchan_info->cfreq2;
+
+                       if (tchan_info->is_chan_passive)
+                               chan_info->info |= WMI_CHAN_INFO_PASSIVE;
+                       if (tchan_info->allow_he)
+                               chan_info->info |= WMI_CHAN_INFO_ALLOW_HE;
+                       else if (tchan_info->allow_vht)
+                               chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT;
+                       else if (tchan_info->allow_ht)
+                               chan_info->info |= WMI_CHAN_INFO_ALLOW_HT;
+                       if (tchan_info->half_rate)
+                               chan_info->info |= WMI_CHAN_INFO_HALF_RATE;
+                       if (tchan_info->quarter_rate)
+                               chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE;
+                       if (tchan_info->psc_channel)
+                               chan_info->info |= WMI_CHAN_INFO_PSC;
+
+                       chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE,
+                                                     tchan_info->phy_mode);
+                       *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR,
+                                           tchan_info->minpower);
+                       *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR,
+                                           tchan_info->maxpower);
+                       *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR,
+                                           tchan_info->maxregpower);
+                       *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS,
+                                           tchan_info->reg_class_id);
+                       *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX,
+                                           tchan_info->antennamax);
+
+                       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                                  "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n",
+                                  i, chan_info->mhz, chan_info->info);
+
+                       ptr += sizeof(*chan_info);
+
+                       tchan_info++;
+               }
 
-               tchan_info++;
-       }
+               ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SCAN_CHAN_LIST_CMDID);
+               if (ret) {
+                       ath11k_warn(ar->ab, "failed to send WMI_SCAN_CHAN_LIST cmd\n");
+                       dev_kfree_skb(skb);
+                       return ret;
+               }
 
-       ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SCAN_CHAN_LIST_CMDID);
-       if (ret) {
-               ath11k_warn(ar->ab, "failed to send WMI_SCAN_CHAN_LIST cmd\n");
-               dev_kfree_skb(skb);
+               num_sends++;
        }
 
-       return ret;
+       return 0;
 }
 
 int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id,
@@ -3265,6 +3398,236 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab)
        return ath11k_init_cmd_send(&wmi_sc->wmi[0], &init_param);
 }
 
+int ath11k_wmi_vdev_spectral_conf(struct ath11k *ar,
+                                 struct ath11k_wmi_vdev_spectral_conf_param *param)
+{
+       struct ath11k_wmi_vdev_spectral_conf_cmd *cmd;
+       struct sk_buff *skb;
+       int ret;
+
+       skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct ath11k_wmi_vdev_spectral_conf_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                    WMI_TAG_VDEV_SPECTRAL_CONFIGURE_CMD) |
+                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+       memcpy(&cmd->param, param, sizeof(*param));
+
+       ret = ath11k_wmi_cmd_send(ar->wmi, skb,
+                                 WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID);
+       if (ret) {
+               ath11k_warn(ar->ab,
+                           "failed to send spectral scan config wmi cmd\n");
+               goto err;
+       }
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "WMI spectral scan config cmd vdev_id 0x%x\n",
+                  param->vdev_id);
+
+       return 0;
+err:
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+int ath11k_wmi_vdev_spectral_enable(struct ath11k *ar, u32 vdev_id,
+                                   u32 trigger, u32 enable)
+{
+       struct ath11k_wmi_vdev_spectral_enable_cmd *cmd;
+       struct sk_buff *skb;
+       int ret;
+
+       skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct ath11k_wmi_vdev_spectral_enable_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                    WMI_TAG_VDEV_SPECTRAL_ENABLE_CMD) |
+                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+       cmd->vdev_id = vdev_id;
+       cmd->trigger_cmd = trigger;
+       cmd->enable_cmd = enable;
+
+       ret = ath11k_wmi_cmd_send(ar->wmi, skb,
+                                 WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID);
+       if (ret) {
+               ath11k_warn(ar->ab,
+                           "failed to send spectral enable wmi cmd\n");
+               goto err;
+       }
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "WMI spectral enable cmd vdev id 0x%x\n",
+                  vdev_id);
+
+       return 0;
+err:
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+int ath11k_wmi_pdev_dma_ring_cfg(struct ath11k *ar,
+                                struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *param)
+{
+       struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *cmd;
+       struct sk_buff *skb;
+       int ret;
+
+       skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_DMA_RING_CFG_REQ) |
+                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+       cmd->pdev_id            = param->pdev_id;
+       cmd->module_id          = param->module_id;
+       cmd->base_paddr_lo      = param->base_paddr_lo;
+       cmd->base_paddr_hi      = param->base_paddr_hi;
+       cmd->head_idx_paddr_lo  = param->head_idx_paddr_lo;
+       cmd->head_idx_paddr_hi  = param->head_idx_paddr_hi;
+       cmd->tail_idx_paddr_lo  = param->tail_idx_paddr_lo;
+       cmd->tail_idx_paddr_hi  = param->tail_idx_paddr_hi;
+       cmd->num_elems          = param->num_elems;
+       cmd->buf_size           = param->buf_size;
+       cmd->num_resp_per_event = param->num_resp_per_event;
+       cmd->event_timeout_ms   = param->event_timeout_ms;
+
+       ret = ath11k_wmi_cmd_send(ar->wmi, skb,
+                                 WMI_PDEV_DMA_RING_CFG_REQ_CMDID);
+       if (ret) {
+               ath11k_warn(ar->ab,
+                           "failed to send dma ring cfg req wmi cmd\n");
+               goto err;
+       }
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "WMI DMA ring cfg req cmd pdev_id 0x%x\n",
+                  param->pdev_id);
+
+       return 0;
+err:
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+static int ath11k_wmi_tlv_dma_buf_entry_parse(struct ath11k_base *soc,
+                                             u16 tag, u16 len,
+                                             const void *ptr, void *data)
+{
+       struct wmi_tlv_dma_buf_release_parse *parse = data;
+
+       if (tag != WMI_TAG_DMA_BUF_RELEASE_ENTRY)
+               return -EPROTO;
+
+       if (parse->num_buf_entry >= parse->fixed.num_buf_release_entry)
+               return -ENOBUFS;
+
+       parse->num_buf_entry++;
+       return 0;
+}
+
+static int ath11k_wmi_tlv_dma_buf_meta_parse(struct ath11k_base *soc,
+                                            u16 tag, u16 len,
+                                            const void *ptr, void *data)
+{
+       struct wmi_tlv_dma_buf_release_parse *parse = data;
+
+       if (tag != WMI_TAG_DMA_BUF_RELEASE_SPECTRAL_META_DATA)
+               return -EPROTO;
+
+       if (parse->num_meta >= parse->fixed.num_meta_data_entry)
+               return -ENOBUFS;
+
+       parse->num_meta++;
+       return 0;
+}
+
+static int ath11k_wmi_tlv_dma_buf_parse(struct ath11k_base *ab,
+                                       u16 tag, u16 len,
+                                       const void *ptr, void *data)
+{
+       struct wmi_tlv_dma_buf_release_parse *parse = data;
+       int ret;
+
+       switch (tag) {
+       case WMI_TAG_DMA_BUF_RELEASE:
+               memcpy(&parse->fixed, ptr,
+                      sizeof(struct ath11k_wmi_dma_buf_release_fixed_param));
+               parse->fixed.pdev_id = DP_HW2SW_MACID(parse->fixed.pdev_id);
+               break;
+       case WMI_TAG_ARRAY_STRUCT:
+               if (!parse->buf_entry_done) {
+                       parse->num_buf_entry = 0;
+                       parse->buf_entry = (struct wmi_dma_buf_release_entry *)ptr;
+
+                       ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+                                                 ath11k_wmi_tlv_dma_buf_entry_parse,
+                                                 parse);
+                       if (ret) {
+                               ath11k_warn(ab, "failed to parse dma buf entry tlv %d\n",
+                                           ret);
+                               return ret;
+                       }
+
+                       parse->buf_entry_done = true;
+               } else if (!parse->meta_data_done) {
+                       parse->num_meta = 0;
+                       parse->meta_data = (struct wmi_dma_buf_release_meta_data *)ptr;
+
+                       ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+                                                 ath11k_wmi_tlv_dma_buf_meta_parse,
+                                                 parse);
+                       if (ret) {
+                               ath11k_warn(ab, "failed to parse dma buf meta tlv %d\n",
+                                           ret);
+                               return ret;
+                       }
+
+                       parse->meta_data_done = true;
+               }
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static void ath11k_wmi_pdev_dma_ring_buf_release_event(struct ath11k_base *ab,
+                                                      struct sk_buff *skb)
+{
+       struct wmi_tlv_dma_buf_release_parse parse = { };
+       struct ath11k_dbring_buf_release_event param;
+       int ret;
+
+       ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+                                 ath11k_wmi_tlv_dma_buf_parse,
+                                 &parse);
+       if (ret) {
+               ath11k_warn(ab, "failed to parse dma buf release tlv %d\n", ret);
+               return;
+       }
+
+       param.fixed             = parse.fixed;
+       param.buf_entry         = parse.buf_entry;
+       param.num_buf_entry     = parse.num_buf_entry;
+       param.meta_data         = parse.meta_data;
+       param.num_meta          = parse.num_meta;
+
+       ret = ath11k_dbring_buffer_release_event(ab, &param);
+       if (ret) {
+               ath11k_warn(ab, "failed to handle dma buf release event %d\n", ret);
+               return;
+       }
+}
+
 static int ath11k_wmi_tlv_hw_mode_caps_parse(struct ath11k_base *soc,
                                             u16 tag, u16 len,
                                             const void *ptr, void *data)
@@ -3445,6 +3808,95 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,
        return 0;
 }
 
+static int ath11k_wmi_tlv_dma_ring_caps_parse(struct ath11k_base *soc,
+                                             u16 tag, u16 len,
+                                             const void *ptr, void *data)
+{
+       struct wmi_tlv_dma_ring_caps_parse *parse = data;
+
+       if (tag != WMI_TAG_DMA_RING_CAPABILITIES)
+               return -EPROTO;
+
+       parse->n_dma_ring_caps++;
+       return 0;
+}
+
+static int ath11k_wmi_alloc_dbring_caps(struct ath11k_base *ab,
+                                       u32 num_cap)
+{
+       size_t sz;
+       void *ptr;
+
+       sz = num_cap * sizeof(struct ath11k_dbring_cap);
+       ptr = kzalloc(sz, GFP_ATOMIC);
+       if (!ptr)
+               return -ENOMEM;
+
+       ab->db_caps = ptr;
+       ab->num_db_cap = num_cap;
+
+       return 0;
+}
+
+static void ath11k_wmi_free_dbring_caps(struct ath11k_base *ab)
+{
+       kfree(ab->db_caps);
+       ab->db_caps = NULL;
+}
+
+static int ath11k_wmi_tlv_dma_ring_caps(struct ath11k_base *ab,
+                                       u16 len, const void *ptr, void *data)
+{
+       struct wmi_tlv_dma_ring_caps_parse *dma_caps_parse = data;
+       struct wmi_dma_ring_capabilities *dma_caps;
+       struct ath11k_dbring_cap *dir_buff_caps;
+       int ret;
+       u32 i;
+
+       dma_caps_parse->n_dma_ring_caps = 0;
+       dma_caps = (struct wmi_dma_ring_capabilities *)ptr;
+       ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+                                 ath11k_wmi_tlv_dma_ring_caps_parse,
+                                 dma_caps_parse);
+       if (ret) {
+               ath11k_warn(ab, "failed to parse dma ring caps tlv %d\n", ret);
+               return ret;
+       }
+
+       if (!dma_caps_parse->n_dma_ring_caps)
+               return 0;
+
+       if (ab->num_db_cap) {
+               ath11k_warn(ab, "Already processed, so ignoring dma ring caps\n");
+               return 0;
+       }
+
+       ret = ath11k_wmi_alloc_dbring_caps(ab, dma_caps_parse->n_dma_ring_caps);
+       if (ret)
+               return ret;
+
+       dir_buff_caps = ab->db_caps;
+       for (i = 0; i < dma_caps_parse->n_dma_ring_caps; i++) {
+               if (dma_caps[i].module_id >= WMI_DIRECT_BUF_MAX) {
+                       ath11k_warn(ab, "Invalid module id %d\n", dma_caps[i].module_id);
+                       ret = -EINVAL;
+                       goto free_dir_buff;
+               }
+
+               dir_buff_caps[i].id = dma_caps[i].module_id;
+               dir_buff_caps[i].pdev_id = DP_HW2SW_MACID(dma_caps[i].pdev_id);
+               dir_buff_caps[i].min_elem = dma_caps[i].min_elem;
+               dir_buff_caps[i].min_buf_sz = dma_caps[i].min_buf_sz;
+               dir_buff_caps[i].min_buf_align = dma_caps[i].min_buf_align;
+       }
+
+       return 0;
+
+free_dir_buff:
+       ath11k_wmi_free_dbring_caps(ab);
+       return ret;
+}
+
 static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab,
                                            u16 tag, u16 len,
                                            const void *ptr, void *data)
@@ -3501,7 +3953,19 @@ static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab,
                                return ret;
 
                        svc_rdy_ext->ext_hal_reg_done = true;
-                       complete(&ab->wmi_ab.service_ready);
+               } else if (!svc_rdy_ext->mac_phy_chainmask_combo_done) {
+                       svc_rdy_ext->mac_phy_chainmask_combo_done = true;
+               } else if (!svc_rdy_ext->mac_phy_chainmask_cap_done) {
+                       svc_rdy_ext->mac_phy_chainmask_cap_done = true;
+               } else if (!svc_rdy_ext->oem_dma_ring_cap_done) {
+                       svc_rdy_ext->oem_dma_ring_cap_done = true;
+               } else if (!svc_rdy_ext->dma_ring_cap_done) {
+                       ret = ath11k_wmi_tlv_dma_ring_caps(ab, len, ptr,
+                                                          &svc_rdy_ext->dma_caps_parse);
+                       if (ret)
+                               return ret;
+
+                       svc_rdy_ext->dma_ring_cap_done = true;
                }
                break;
 
@@ -3522,11 +3986,66 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab,
                                  &svc_rdy_ext);
        if (ret) {
                ath11k_warn(ab, "failed to parse tlv %d\n", ret);
-               return ret;
+               goto err;
        }
 
+       if (!test_bit(WMI_TLV_SERVICE_EXT2_MSG, ab->wmi_ab.svc_map))
+               complete(&ab->wmi_ab.service_ready);
+
        kfree(svc_rdy_ext.mac_phy_caps);
        return 0;
+
+err:
+       ath11k_wmi_free_dbring_caps(ab);
+       return ret;
+}
+
+static int ath11k_wmi_tlv_svc_rdy_ext2_parse(struct ath11k_base *ab,
+                                            u16 tag, u16 len,
+                                            const void *ptr, void *data)
+{
+       struct wmi_tlv_svc_rdy_ext2_parse *parse = data;
+       int ret;
+
+       switch (tag) {
+       case WMI_TAG_ARRAY_STRUCT:
+               if (!parse->dma_ring_cap_done) {
+                       ret = ath11k_wmi_tlv_dma_ring_caps(ab, len, ptr,
+                                                          &parse->dma_caps_parse);
+                       if (ret)
+                               return ret;
+
+                       parse->dma_ring_cap_done = true;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int ath11k_service_ready_ext2_event(struct ath11k_base *ab,
+                                          struct sk_buff *skb)
+{
+       struct wmi_tlv_svc_rdy_ext2_parse svc_rdy_ext2 = { };
+       int ret;
+
+       ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+                                 ath11k_wmi_tlv_svc_rdy_ext2_parse,
+                                 &svc_rdy_ext2);
+       if (ret) {
+               ath11k_warn(ab, "failed to parse ext2 event tlv %d\n", ret);
+               goto err;
+       }
+
+       complete(&ab->wmi_ab.service_ready);
+
+       return 0;
+
+err:
+       ath11k_wmi_free_dbring_caps(ab);
+       return ret;
 }
 
 static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buff *skb,
@@ -3822,6 +4341,7 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab,
        }
 
        hdr->pdev_id =  ev->pdev_id;
+       hdr->chan_freq = ev->chan_freq;
        hdr->channel =  ev->channel;
        hdr->snr =  ev->snr;
        hdr->rate =  ev->rate;
@@ -5193,7 +5713,9 @@ static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb)
        if (rx_ev.status & WMI_RX_STATUS_ERR_MIC)
                status->flag |= RX_FLAG_MMIC_ERROR;
 
-       if (rx_ev.channel >= 1 && rx_ev.channel <= 14) {
+       if (rx_ev.chan_freq >= ATH11K_MIN_6G_FREQ) {
+               status->band = NL80211_BAND_6GHZ;
+       } else if (rx_ev.channel >= 1 && rx_ev.channel <= 14) {
                status->band = NL80211_BAND_2GHZ;
        } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH11K_MAX_5G_CHAN) {
                status->band = NL80211_BAND_5GHZ;
@@ -5206,9 +5728,10 @@ static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb)
                goto exit;
        }
 
-       if (rx_ev.phy_mode == MODE_11B && status->band == NL80211_BAND_5GHZ)
+       if (rx_ev.phy_mode == MODE_11B &&
+           (status->band == NL80211_BAND_5GHZ || status->band == NL80211_BAND_6GHZ))
                ath11k_dbg(ab, ATH11K_DBG_WMI,
-                          "wmi mgmt rx 11b (CCK) on 5GHz\n");
+                          "wmi mgmt rx 11b (CCK) on 5/6GHz, band = %d\n", status->band);
 
        sband = &ar->mac.sbands[status->band];
 
@@ -5933,6 +6456,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
        case WMI_SERVICE_READY_EXT_EVENTID:
                ath11k_service_ready_ext_event(ab, skb);
                break;
+       case WMI_SERVICE_READY_EXT2_EVENTID:
+               ath11k_service_ready_ext2_event(ab, skb);
+               break;
        case WMI_REG_CHAN_LIST_CC_EVENTID:
                ath11k_reg_chan_list_event(ab, skb);
                break;
@@ -5994,12 +6520,16 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
        case WMI_PDEV_TEMPERATURE_EVENTID:
                ath11k_wmi_pdev_temperature_event(ab, skb);
                break;
+       case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID:
+               ath11k_wmi_pdev_dma_ring_buf_release_event(ab, skb);
+               break;
        /* add Unsupported events here */
        case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
        case WMI_VDEV_DELETE_RESP_EVENTID:
        case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
        case WMI_TWT_ENABLE_EVENTID:
        case WMI_TWT_DISABLE_EVENTID:
+       case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID:
                ath11k_dbg(ab, ATH11K_DBG_WMI,
                           "ignoring unsupported event 0x%x\n", id);
                break;
@@ -6213,4 +6743,6 @@ void ath11k_wmi_detach(struct ath11k_base *ab)
 
        for (i = 0; i < ab->htc.wmi_ep_count; i++)
                ath11k_wmi_pdev_detach(ab, i);
+
+       ath11k_wmi_free_dbring_caps(ab);
 }
index b9f3e55..5a32ba0 100644 (file)
@@ -24,6 +24,8 @@ struct ath11k_fw_stats;
 #define HE_PET_8_USEC            1
 #define HE_PET_16_USEC           2
 
+#define WMI_MAX_CHAINS          8
+
 #define WMI_MAX_NUM_SS                    MAX_HE_NSS
 #define WMI_MAX_NUM_RU                    MAX_HE_RU
 
@@ -50,10 +52,20 @@ struct wmi_tlv {
 #define WMI_MAX_MEM_REQS        32
 #define ATH11K_MAX_HW_LISTEN_INTERVAL 5
 
+#define WLAN_SCAN_MAX_HINT_S_SSID        10
+#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 WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1
+
 #define WMI_BA_MODE_BUFFER_SIZE_256  3
 /*
  * HW mode config type replicated from FW header
@@ -586,6 +598,11 @@ enum wmi_tlv_event_id {
        WMI_PDEV_DMA_RING_CFG_RSP_EVENTID,
        WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID,
        WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID,
+       WMI_PDEV_CSC_SWITCH_COUNT_STATUS_EVENTID,
+       WMI_PDEV_COLD_BOOT_CAL_DATA_EVENTID,
+       WMI_PDEV_RAP_INFO_EVENTID,
+       WMI_CHAN_RF_CHARACTERIZATION_INFO_EVENTID,
+       WMI_SERVICE_READY_EXT2_EVENTID,
        WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV),
        WMI_VDEV_STOPPED_EVENTID,
        WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
@@ -1011,6 +1028,7 @@ enum wmi_tlv_vdev_param {
        WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME,
        WMI_VDEV_PARAM_BA_MODE = 0x7e,
        WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87,
+       WMI_VDEV_PARAM_6GHZ_PARAMS = 0x99,
        WMI_VDEV_PARAM_PROTOTYPE = 0x8000,
        WMI_VDEV_PARAM_BSS_COLOR,
        WMI_VDEV_PARAM_SET_HEMU_MODE,
@@ -2013,9 +2031,10 @@ enum wmi_tlv_service {
        WMI_TLV_SERVICE_DSM_ROAM_FILTER = 211,
        WMI_TLV_SERVICE_PACKET_CAPTURE_SUPPORT = 212,
        WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213,
+       WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219,
+       WMI_TLV_SERVICE_EXT2_MSG = 220,
 
        WMI_MAX_EXT_SERVICE
-
 };
 
 enum {
@@ -2076,6 +2095,14 @@ enum wmi_beacon_gen_mode {
        WMI_BEACON_BURST_MODE = 1
 };
 
+enum wmi_direct_buffer_module {
+       WMI_DIRECT_BUF_SPECTRAL = 0,
+       WMI_DIRECT_BUF_CFR = 1,
+
+       /* keep it last */
+       WMI_DIRECT_BUF_MAX
+};
+
 struct wmi_host_pdev_band_to_mac {
        u32 pdev_id;
        u32 start_freq;
@@ -2382,6 +2409,15 @@ struct wmi_mac_addr {
        } __packed;
 } __packed;
 
+struct wmi_dma_ring_capabilities {
+       u32 tlv_header;
+       u32 pdev_id;
+       u32 module_id;
+       u32 min_elem;
+       u32 min_buf_sz;
+       u32 min_buf_align;
+} __packed;
+
 struct wmi_ready_event_min {
        struct wmi_abi_version fw_abi_vers;
        struct wmi_mac_addr mac_addr;
@@ -2519,7 +2555,8 @@ struct channel_param {
            allow_ht:1,
            allow_vht:1,
            allow_he:1,
-           set_agile:1;
+           set_agile:1,
+           psc_channel:1;
        u32 phy_mode;
        u32 cfreq1;
        u32 cfreq2;
@@ -3059,6 +3096,9 @@ struct  wmi_start_scan_cmd {
        u32 num_vendor_oui;
        u32 scan_ctrl_flags_ext;
        u32 dwell_time_active_2g;
+       u32 dwell_time_active_6g;
+       u32 dwell_time_passive_6g;
+       u32 scan_start_offset;
 } __packed;
 
 #define WMI_SCAN_FLAG_PASSIVE        0x1
@@ -3098,6 +3138,16 @@ enum {
        ((flag) |= (((mode) << WMI_SCAN_DWELL_MODE_SHIFT) & \
                    WMI_SCAN_DWELL_MODE_MASK))
 
+struct hint_short_ssid {
+       u32 freq_flags;
+       u32 short_ssid;
+};
+
+struct hint_bssid {
+       u32 freq_flags;
+       struct wmi_mac_addr bssid;
+};
+
 struct scan_req_params {
        u32 scan_id;
        u32 scan_req_id;
@@ -3125,6 +3175,8 @@ struct scan_req_params {
        u32 dwell_time_active;
        u32 dwell_time_active_2g;
        u32 dwell_time_passive;
+       u32 dwell_time_active_6g;
+       u32 dwell_time_passive_6g;
        u32 min_rest_time;
        u32 max_rest_time;
        u32 repeat_probe_time;
@@ -3175,6 +3227,10 @@ struct scan_req_params {
        struct element_info extraie;
        struct element_info htcap;
        struct element_info vhtcap;
+       u32 num_hint_s_ssid;
+       u32 num_hint_bssid;
+       struct hint_short_ssid hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID];
+       struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID];
 };
 
 struct wmi_ssid_arg {
@@ -3264,6 +3320,7 @@ struct  wmi_bcn_send_from_host_cmd {
 #define WMI_CHAN_INFO_QUARTER_RATE     BIT(15)
 #define WMI_CHAN_INFO_DFS_FREQ2                BIT(16)
 #define WMI_CHAN_INFO_ALLOW_HE         BIT(17)
+#define WMI_CHAN_INFO_PSC              BIT(18)
 
 #define WMI_CHAN_REG_INFO1_MIN_PWR     GENMASK(7, 0)
 #define WMI_CHAN_REG_INFO1_MAX_PWR     GENMASK(15, 8)
@@ -3444,6 +3501,7 @@ struct peer_assoc_params {
        u32 tx_max_rate;
        u32 tx_mcs_set;
        u8 vht_capable;
+       u8 min_data_rate;
        u32 tx_max_mcs_nss;
        u32 peer_bw_rxnss_override;
        bool is_pmf_enabled;
@@ -3472,6 +3530,7 @@ struct peer_assoc_params {
        bool he_flag;
        u32 peer_he_cap_macinfo[2];
        u32 peer_he_cap_macinfo_internal;
+       u32 peer_he_caps_6ghz;
        u32 peer_he_ops;
        u32 peer_he_cap_phyinfo[WMI_HOST_MAX_HECAP_PHY_SIZE];
        u32 peer_he_mcs_count;
@@ -3509,6 +3568,8 @@ struct  wmi_peer_assoc_complete_cmd {
        u32 peer_he_mcs;
        u32 peer_he_cap_info_ext;
        u32 peer_he_cap_info_internal;
+       u32 min_data_rate;
+       u32 peer_he_caps_6ghz;
 } __packed;
 
 struct wmi_stop_scan_cmd {
@@ -4228,6 +4289,7 @@ struct wmi_pdev_temperature_event {
 #define WLAN_MGMT_TXRX_HOST_MAX_ANTENNA 4
 
 struct mgmt_rx_event_params {
+       u32 chan_freq;
        u32 channel;
        u32 snr;
        u8 rssi_ctl[WLAN_MGMT_TXRX_HOST_MAX_ANTENNA];
@@ -4257,6 +4319,7 @@ struct wmi_mgmt_rx_hdr {
        u32 rx_tsf_l32;
        u32 rx_tsf_u32;
        u32 pdev_id;
+       u32 chan_freq;
 } __packed;
 
 #define MAX_ANTENNA_EIGHT 8
@@ -4734,6 +4797,117 @@ struct ath11k_wmi_pdev_lro_config_cmd {
        u32 pdev_id;
 } __packed;
 
+#define ATH11K_WMI_SPECTRAL_COUNT_DEFAULT                 0
+#define ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT              224
+#define ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT              1
+#define ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT              7
+#define ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT                1
+#define ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT           0
+#define ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT     -96
+#define ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT           80
+#define ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT          12
+#define ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT           8
+#define ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT           0
+#define ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT         0
+#define ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT           0xf0
+#define ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT            0
+#define ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT              2
+#define ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT             1
+#define ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT               1
+#define ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT              1
+
+struct ath11k_wmi_vdev_spectral_conf_param {
+       u32 vdev_id;
+       u32 scan_count;
+       u32 scan_period;
+       u32 scan_priority;
+       u32 scan_fft_size;
+       u32 scan_gc_ena;
+       u32 scan_restart_ena;
+       u32 scan_noise_floor_ref;
+       u32 scan_init_delay;
+       u32 scan_nb_tone_thr;
+       u32 scan_str_bin_thr;
+       u32 scan_wb_rpt_mode;
+       u32 scan_rssi_rpt_mode;
+       u32 scan_rssi_thr;
+       u32 scan_pwr_format;
+       u32 scan_rpt_mode;
+       u32 scan_bin_scale;
+       u32 scan_dbm_adj;
+       u32 scan_chn_mask;
+} __packed;
+
+struct ath11k_wmi_vdev_spectral_conf_cmd {
+       u32 tlv_header;
+       struct ath11k_wmi_vdev_spectral_conf_param param;
+} __packed;
+
+#define ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER  1
+#define ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR    2
+#define ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE    1
+#define ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE   2
+
+struct ath11k_wmi_vdev_spectral_enable_cmd {
+       u32 tlv_header;
+       u32 vdev_id;
+       u32 trigger_cmd;
+       u32 enable_cmd;
+} __packed;
+
+struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd {
+       u32 tlv_header;
+       u32 pdev_id;
+       u32 module_id;          /* see enum wmi_direct_buffer_module */
+       u32 base_paddr_lo;
+       u32 base_paddr_hi;
+       u32 head_idx_paddr_lo;
+       u32 head_idx_paddr_hi;
+       u32 tail_idx_paddr_lo;
+       u32 tail_idx_paddr_hi;
+       u32 num_elems;          /* Number of elems in the ring */
+       u32 buf_size;           /* size of allocated buffer in bytes */
+
+       /* Number of wmi_dma_buf_release_entry packed together */
+       u32 num_resp_per_event;
+
+       /* Target should timeout and send whatever resp
+        * it has if this time expires, units in milliseconds
+        */
+       u32 event_timeout_ms;
+} __packed;
+
+struct ath11k_wmi_dma_buf_release_fixed_param {
+       u32 pdev_id;
+       u32 module_id;
+       u32 num_buf_release_entry;
+       u32 num_meta_data_entry;
+} __packed;
+
+struct wmi_dma_buf_release_entry {
+       u32 tlv_header;
+       u32 paddr_lo;
+
+       /* Bits 11:0:   address of data
+        * Bits 31:12:  host context data
+        */
+       u32 paddr_hi;
+} __packed;
+
+#define WMI_SPECTRAL_META_INFO1_FREQ1          GENMASK(15, 0)
+#define WMI_SPECTRAL_META_INFO1_FREQ2          GENMASK(31, 16)
+
+#define WMI_SPECTRAL_META_INFO2_CHN_WIDTH      GENMASK(7, 0)
+
+struct wmi_dma_buf_release_meta_data {
+       u32 tlv_header;
+       s32 noise_floor[WMI_MAX_CHAINS];
+       u32 reset_delay;
+       u32 freq1;
+       u32 freq2;
+       u32 ch_width;
+} __packed;
+
 struct target_resource_config {
        u32 num_vdevs;
        u32 num_peers;
@@ -4941,4 +5115,10 @@ int ath11k_wmi_send_obss_color_collision_cfg_cmd(struct ath11k *ar, u32 vdev_id,
 int ath11k_wmi_send_bss_color_change_enable_cmd(struct ath11k *ar, u32 vdev_id,
                                                bool enable);
 int ath11k_wmi_pdev_lro_cfg(struct ath11k *ar, int pdev_id);
+int ath11k_wmi_pdev_dma_ring_cfg(struct ath11k *ar,
+                                struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *param);
+int ath11k_wmi_vdev_spectral_enable(struct ath11k *ar, u32 vdev_id,
+                                   u32 trigger, u32 enable);
+int ath11k_wmi_vdev_spectral_conf(struct ath11k *ar,
+                                 struct ath11k_wmi_vdev_spectral_conf_param *param);
 #endif
index 42bfdb4..d5e9af2 100644 (file)
@@ -34,7 +34,7 @@ config ATH9K
          APs that come with these cards refer to ath9k wiki
          products page:
 
-         http://wireless.kernel.org/en/users/Drivers/ath9k/products
+         https://wireless.wiki.kernel.org/en/users/Drivers/ath9k/products
 
          If you choose to build a module, it'll be called ath9k.
 
@@ -185,7 +185,8 @@ config ATH9K_HTC
          Support for Atheros HTC based cards.
          Chipsets supported: AR9271
 
-         For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc
+         For more information:
+         https://wireless.wiki.kernel.org/en/users/Drivers/ath9k_htc
 
          The built module will be ath9k_htc.
 
index 052deff..8c97db7 100644 (file)
@@ -2410,7 +2410,7 @@ static u8 fixup_chainmask(u8 chip_chainmask, u8 eeprom_chainmask)
  * of tests. The testing requirements are going to be documented. Desired
  * test requirements are documented at:
  *
- * http://wireless.kernel.org/en/users/Drivers/ath9k/dfs
+ * https://wireless.wiki.kernel.org/en/users/Drivers/ath9k/dfs
  *
  * Once a new chipset gets properly tested an individual commit can be used
  * to document the testing for DFS for that chipset.
index b1bce7a..b2d7608 100644 (file)
@@ -10,7 +10,7 @@ config CARL9170
 
          It needs a special firmware (carl9170-1.fw), which can be downloaded
          from our wiki here:
-         <http://wireless.kernel.org/en/users/Drivers/carl9170>
+         <https://wireless.wiki.kernel.org/en/users/Drivers/carl9170>
 
          If you choose to build a module, it'll be called carl9170.
 
index 486957a..ead7933 100644 (file)
@@ -61,7 +61,7 @@ MODULE_ALIAS("arusb_lnx");
  * Note:
  *
  * Always update our wiki's device list (located at:
- * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ),
+ * https://wireless.wiki.kernel.org/en/users/Drivers/ar9170/devices ),
  * whenever you add a new device.
  */
 static const struct usb_device_id carl9170_usb_ids[] = {
index 0d742ac..9c2e545 100644 (file)
@@ -24,6 +24,7 @@
  * could be acquired so far.
  */
 #define SPECTRAL_ATH10K_MAX_NUM_BINS           256
+#define SPECTRAL_ATH11K_MAX_NUM_BINS           512
 
 /* FFT sample format given to userspace via debugfs.
  *
@@ -37,6 +38,7 @@ enum ath_fft_sample_type {
        ATH_FFT_SAMPLE_HT20 = 1,
        ATH_FFT_SAMPLE_HT20_40,
        ATH_FFT_SAMPLE_ATH10K,
+       ATH_FFT_SAMPLE_ATH11K
 };
 
 struct fft_sample_tlv {
@@ -110,4 +112,19 @@ struct fft_sample_ath10k {
        u8 data[0];
 } __packed;
 
+struct fft_sample_ath11k {
+       struct fft_sample_tlv tlv;
+       u8 chan_width_mhz;
+       s8 max_index;
+       u8 max_exp;
+       __be16 freq1;
+       __be16 freq2;
+       __be16 max_magnitude;
+       __be16 rssi;
+       __be32 tsf;
+       __be32 noise;
+
+       u8 data[0];
+} __packed;
+
 #endif /* SPECTRAL_COMMON_H */
index dadba2d..6a95b19 100644 (file)
@@ -10,7 +10,7 @@ config WIL6210
          wil6210 chip by Wilocity. It supports operation on the
          60 GHz band, covered by the IEEE802.11ad standard.
 
-         http://wireless.kernel.org/en/users/Drivers/wil6210
+         https://wireless.wiki.kernel.org/en/users/Drivers/wil6210
 
          If you choose to build it as a module, it will be called
          wil6210
index da6d420..ad9d7e7 100644 (file)
@@ -1415,7 +1415,7 @@ il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
 /*
  * mac80211 queues, ACs, hardware queues, FIFOs.
  *
- * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues
+ * Cf. https://wireless.wiki.kernel.org/en/developers/Documentation/mac80211/queues
  *
  * Mac80211 uses the following numbers, which we get as from it
  * by way of skb_get_queue_mapping(skb):
index 36153fa..1085afb 100644 (file)
@@ -31,7 +31,7 @@ config IWLWIFI
          In order to use this driver, you will need a firmware
          image for it. You can obtain the microcode from:
 
-                 <http://wireless.kernel.org/en/users/Drivers/iwlwifi>.
+                 <https://wireless.wiki.kernel.org/en/users/Drivers/iwlwifi>.
 
          The firmware is typically installed in /lib/firmware. You can
          look in the hotplug script /etc/hotplug/firmware.agent to
index 0f4be4b..fdcc129 100644 (file)
@@ -1023,7 +1023,7 @@ struct iwl_wep_cmd {
        u8 global_key_type;
        u8 flags;
        u8 reserved;
-       struct iwl_wep_key key[0];
+       struct iwl_wep_key key[];
 } __packed;
 
 #define WEP_KEY_WEP_TYPE 1
@@ -1305,7 +1305,7 @@ struct iwl_tx_cmd {
         * length is 26 or 30 bytes, followed by payload data
         */
        u8 payload[0];
-       struct ieee80211_hdr hdr[0];
+       struct ieee80211_hdr hdr[];
 } __packed;
 
 /*
@@ -2380,7 +2380,7 @@ struct iwl_scan_cmd {
         * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
         * before requesting another scan.
         */
-       u8 data[0];
+       u8 data[];
 } __packed;
 
 /* Can abort will notify by complete notification with abort status. */
@@ -2475,7 +2475,7 @@ struct iwl_tx_beacon_cmd {
        __le16 tim_idx;
        u8 tim_size;
        u8 reserved1;
-       struct ieee80211_hdr frame[0];  /* beacon frame */
+       struct ieee80211_hdr frame[];   /* beacon frame */
 } __packed;
 
 /******************************************************************************
@@ -3188,7 +3188,7 @@ struct iwl_calib_hdr {
 
 struct iwl_calib_cmd {
        struct iwl_calib_hdr hdr;
-       u8 data[0];
+       u8 data[];
 } __packed;
 
 struct iwl_calib_xtal_freq_cmd {
@@ -3216,7 +3216,7 @@ struct iwl_calib_temperature_offset_v2_cmd {
 /* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
 struct iwl_calib_chain_noise_reset_cmd {
        struct iwl_calib_hdr hdr;
-       u8 data[0];
+       u8 data[];
 };
 
 /* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */
index 6512d25..423d3c3 100644 (file)
@@ -200,6 +200,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
        iwl_leds_init(priv);
 
        wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
+       wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_EXT_KEY_ID);
 
        ret = ieee80211_register_hw(priv->hw);
        if (ret) {
index fd719c3..b6c31f0 100644 (file)
@@ -361,7 +361,7 @@ struct iwl_mcc_update_resp_v3 {
        __le16 time;
        __le16 geo_info;
        __le32 n_channels;
-       __le32 channels[0];
+       __le32 channels[];
 } __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_3 */
 
 /**
@@ -390,7 +390,7 @@ struct iwl_mcc_update_resp {
        u8 source_id;
        u8 reserved[3];
        __le32 n_channels;
-       __le32 channels[0];
+       __le32 channels[];
 } __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_4 */
 
 /**
index f1d1fe9..82d59b5 100644 (file)
@@ -293,7 +293,7 @@ struct iwl_tx_cmd {
        __le16 pm_frame_timeout;
        __le16 reserved4;
        u8 payload[0];
-       struct ieee80211_hdr hdr[0];
+       struct ieee80211_hdr hdr[];
 } __packed; /* TX_CMD_API_S_VER_6 */
 
 struct iwl_dram_sec_info {
@@ -319,7 +319,7 @@ struct iwl_tx_cmd_gen2 {
        __le32 flags;
        struct iwl_dram_sec_info dram_info;
        __le32 rate_n_flags;
-       struct ieee80211_hdr hdr[0];
+       struct ieee80211_hdr hdr[];
 } __packed; /* TX_CMD_API_S_VER_7 */
 
 /**
@@ -342,7 +342,7 @@ struct iwl_tx_cmd_gen3 {
        struct iwl_dram_sec_info dram_info;
        __le32 rate_n_flags;
        __le64 ttl;
-       struct ieee80211_hdr hdr[0];
+       struct ieee80211_hdr hdr[];
 } __packed; /* TX_CMD_API_S_VER_8 */
 
 /*
@@ -766,8 +766,8 @@ struct iwl_mvm_compressed_ba_notif {
        __le32 tx_rate;
        __le16 tfd_cnt;
        __le16 ra_tid_cnt;
-       struct iwl_mvm_compressed_ba_tfd tfd[0];
        struct iwl_mvm_compressed_ba_ratid ra_tid[0];
+       struct iwl_mvm_compressed_ba_tfd tfd[];
 } __packed; /* COMPRESSED_BA_RES_API_S_VER_4 */
 
 /**
@@ -784,7 +784,7 @@ struct iwl_mac_beacon_cmd_v6 {
        __le32 template_id;
        __le32 tim_idx;
        __le32 tim_size;
-       struct ieee80211_hdr frame[0];
+       struct ieee80211_hdr frame[];
 } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_6 */
 
 /**
@@ -805,7 +805,7 @@ struct iwl_mac_beacon_cmd_v7 {
        __le32 tim_size;
        __le32 ecsa_offset;
        __le32 csa_offset;
-       struct ieee80211_hdr frame[0];
+       struct ieee80211_hdr frame[];
 } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */
 
 enum iwl_mac_beacon_flags {
@@ -840,7 +840,7 @@ struct iwl_mac_beacon_cmd {
        __le32 tim_size;
        __le32 ecsa_offset;
        __le32 csa_offset;
-       struct ieee80211_hdr frame[0];
+       struct ieee80211_hdr frame[];
 } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_10 */
 
 struct iwl_beacon_notif {
index 4d3687c..7ea55cf 100644 (file)
@@ -2554,7 +2554,7 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id)
                return -EINVAL;
 
        if (fwrt->dump.conf != FW_DBG_INVALID)
-               IWL_WARN(fwrt, "FW already configured (%d) - re-configuring\n",
+               IWL_INFO(fwrt, "FW already configured (%d) - re-configuring\n",
                         fwrt->dump.conf);
 
        /* Send all HCMDs for configuring the FW debug */
index 6e72c27..267ad4e 100644 (file)
@@ -260,7 +260,7 @@ struct hcmd_write_data {
        __be32 cmd_id;
        __be32 flags;
        __be16 length;
-       u8 data[0];
+       u8 data[];
 } __packed;
 
 static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
index 244899f..e27c132 100644 (file)
@@ -641,6 +641,6 @@ extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0;
 extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long;
 extern const struct iwl_cfg iwlax411_2ax_cfg_sosnj_gf4_a0;
 extern const struct iwl_cfg iwlax211_cfg_snj_gf_a0;
-#endif /* CPTCFG_IWLMVM || CPTCFG_IWLFMAC */
+#endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index 3008a52..b35b892 100644 (file)
@@ -175,7 +175,7 @@ void iwl_opmode_deregister(const char *name);
 struct iwl_op_mode {
        const struct iwl_op_mode_ops *ops;
 
-       char op_mode_specific[0] __aligned(sizeof(void *));
+       char op_mode_specific[] __aligned(sizeof(void *));
 };
 
 static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode)
index a301e24..34788e7 100644 (file)
@@ -1006,7 +1006,7 @@ struct iwl_trans {
 
        /* pointer to trans specific struct */
        /*Ensure that this pointer will always be aligned to sizeof pointer */
-       char trans_specific[0] __aligned(sizeof(void *));
+       char trans_specific[] __aligned(sizeof(void *));
 };
 
 const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id);
index 7791623..9374c85 100644 (file)
@@ -543,6 +543,14 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+
+       /* The new Tx API does not allow to pass the key or keyid of a MPDU to
+        * the hw, preventing us to control which key(id) to use per MPDU.
+        * Till that's fixed we can't use Extended Key ID for the newer cards.
+        */
+       if (!iwl_mvm_has_new_tx_api(mvm))
+               wiphy_ext_feature_set(hw->wiphy,
+                                     NL80211_EXT_FEATURE_EXT_KEY_ID);
        hw->wiphy->features |= NL80211_FEATURE_HT_IBSS;
 
        hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
@@ -4903,7 +4911,7 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
        if (mvmsta->avg_energy) {
-               sinfo->signal_avg = mvmsta->avg_energy;
+               sinfo->signal_avg = -(s8)mvmsta->avg_energy;
                sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
        }
 
index a7264b2..86b2ebb 100644 (file)
@@ -603,7 +603,7 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
                                     struct iwl_lq_sta *lq_data, u8 tid,
                                     struct ieee80211_sta *sta)
 {
-       int ret = -EAGAIN;
+       int ret;
 
        IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
                     sta->addr, tid);
index fee01cb..53af3c9 100644 (file)
@@ -1369,14 +1369,6 @@ out_err:
        return ret;
 }
 
-static inline u8 iwl_mvm_tid_to_ac_queue(int tid)
-{
-       if (tid == IWL_MAX_TID_COUNT)
-               return IEEE80211_AC_VO; /* MGMT */
-
-       return tid_to_mac80211_ac[tid];
-}
-
 void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk)
 {
        struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,