ath10k-ct: add Coverage Class setting ipq40xx and qca99xx WiFi (WIP) master
authorKoen Vandeputte <koen.vandeputte@citymesh.com>
Tue, 21 May 2024 12:49:14 +0000 (14:49 +0200)
committerKoen Vandeputte <koen.vandeputte@citymesh.com>
Tue, 1 Apr 2025 13:58:20 +0000 (15:58 +0200)
Since QCA wireless firmware version 10.4 the Coverage Class can be set
for ipq40xx and qca99xx circuits via the wmi interface.
A timeout value adjusts protocol timing for the extra radio propagation
time that is inherent to transmission over larger than local distances.

In due time an upstream patch in mac80211 can be anticipated.
This patch enables use of ipq40xx SoC based devices in OpenWrt in the
mean time when Coverage Class setting is required.

Authors:
Sebastian Gottschall    <s.gottschall@dd-wrt.com>
William Wortel          <wwortel@dorpstraat.com>

Signed-off-by: William Wortel <wwortel@dorpstraat.com>
Signed-off-by: Koen Vandeputte <koen.vandeputte@citymesh.com>
package/kernel/ath10k-ct/patches/990-ath10k-add-coverage-class-ipq40xx-qca99xx.patch [new file with mode: 0644]

diff --git a/package/kernel/ath10k-ct/patches/990-ath10k-add-coverage-class-ipq40xx-qca99xx.patch b/package/kernel/ath10k-ct/patches/990-ath10k-add-coverage-class-ipq40xx-qca99xx.patch
new file mode 100644 (file)
index 0000000..120b5e6
--- /dev/null
@@ -0,0 +1,1459 @@
+From d38fdb272c4471c5958eaee9ab7d42353871c7a3 Mon Sep 17 00:00:00 2001
+From: Koen Vandeputte <koen.vandeputte@citymesh.com>
+Date: Tue, 25 Mar 2025 14:14:48 +0100
+Subject: [PATCH 1/2] ath10k-ct: add Coverage Class setting ipq40xx and qca99xx
+ WiFi (WIP)
+
+ath10k-ct: add Coverage Class setting ipq40xx and qca99xx WiFi (WIP)
+
+Since QCA wireless firmware version 10.4 the Coverage Class can be set
+for ipq40xx and qca99xx circuits via the wmi interface.
+A timeout value adjusts protocol timing for the extra radio propagation
+time that is inherent to transmission over larger than local distances.
+
+In due time an upstream patch in mac80211 can be anticipated.
+This patch enables use of ipq40xx SoC based devices in OpenWrt in the
+mean time when Coverage Class setting is required.
+
+Authors:
+Sebastian Gottschall    <s.gottschall@dd-wrt.com>
+William Wortel          <wwortel@dorpstraat.com>
+
+Signed-off-by: William Wortel <wwortel@dorpstraat.com>
+Signed-off-by: Koen Vandeputte <koen.vandeputte@citymesh.com>
+---
+ ath10k-5.15/core.c |  2 +-
+ ath10k-5.15/hw.c   | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-5.15/wmi.c  |  4 ++++
+ ath10k-5.15/wmi.h  |  3 +++
+ ath10k-5.17/core.c |  2 +-
+ ath10k-5.17/hw.c   | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-5.17/wmi.c  |  4 ++++
+ ath10k-5.17/wmi.h  |  3 +++
+ ath10k-5.19/core.c |  2 +-
+ ath10k-5.19/hw.c   | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-5.19/wmi.c  |  4 ++++
+ ath10k-5.19/wmi.h  |  3 +++
+ ath10k-6.10/core.c |  2 +-
+ ath10k-6.10/hw.c   | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.10/wmi.c  |  4 ++++
+ ath10k-6.10/wmi.h  |  3 +++
+ ath10k-6.11/core.c |  2 +-
+ ath10k-6.11/hw.c   | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.11/wmi.c  |  4 ++++
+ ath10k-6.11/wmi.h  |  3 +++
+ ath10k-6.14/core.c |  2 +-
+ ath10k-6.14/hw.c   | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.14/wmi.c  |  4 ++++
+ ath10k-6.14/wmi.h  |  3 +++
+ ath10k-6.2/core.c  |  2 +-
+ ath10k-6.2/hw.c    | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.2/wmi.c   |  4 ++++
+ ath10k-6.2/wmi.h   |  3 +++
+ ath10k-6.4/core.c  |  2 +-
+ ath10k-6.4/hw.c    | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.4/wmi.c   |  4 ++++
+ ath10k-6.4/wmi.h   |  3 +++
+ ath10k-6.7/core.c  |  2 +-
+ ath10k-6.7/hw.c    | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.7/wmi.c   |  4 ++++
+ ath10k-6.7/wmi.h   |  3 +++
+ ath10k-6.9/core.c  |  2 +-
+ ath10k-6.9/hw.c    | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ ath10k-6.9/wmi.c   |  4 ++++
+ ath10k-6.9/wmi.h   |  3 +++
+ 40 files changed, 590 insertions(+), 10 deletions(-)
+
+diff --git a/ath10k-5.15/core.c b/ath10k-5.15/core.c
+index c4c96ed..a7d5b0b 100644
+--- a/ath10k-5.15/core.c
++++ b/ath10k-5.15/core.c
+@@ -2970,7 +2970,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-5.15/hw.c b/ath10k-5.15/hw.c
+index 93c3e30..e87fbf6 100644
+--- a/ath10k-5.15/hw.c
++++ b/ath10k-5.15/hw.c
+@@ -584,6 +584,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++                      ath10k_warn(ar, "ath10k core not yet started");
++                      goto unlock;
++      }
++
++        if (value < 0)
++                value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1172,6 +1222,7 @@ static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd)
+ }
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes,
+       .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+diff --git a/ath10k-5.15/wmi.c b/ath10k-5.15/wmi.c
+index c2450df..41e2610 100644
+--- a/ath10k-5.15/wmi.c
++++ b/ath10k-5.15/wmi.c
+@@ -1164,6 +1164,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1260,6 +1261,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1357,6 +1359,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1617,6 +1620,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-5.15/wmi.h b/ath10k-5.15/wmi.h
+index 832a485..3ea179b 100644
+--- a/ath10k-5.15/wmi.h
++++ b/ath10k-5.15/wmi.h
+@@ -3937,6 +3937,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4257,6 +4258,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-5.17/core.c b/ath10k-5.17/core.c
+index b50cb9a..868e14f 100644
+--- a/ath10k-5.17/core.c
++++ b/ath10k-5.17/core.c
+@@ -3035,7 +3035,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-5.17/hw.c b/ath10k-5.17/hw.c
+index 93c3e30..2234b27 100644
+--- a/ath10k-5.17/hw.c
++++ b/ath10k-5.17/hw.c
+@@ -584,6 +584,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1172,6 +1222,7 @@ static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd)
+ }
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes,
+       .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+diff --git a/ath10k-5.17/wmi.c b/ath10k-5.17/wmi.c
+index 5bddfff..1b16957 100644
+--- a/ath10k-5.17/wmi.c
++++ b/ath10k-5.17/wmi.c
+@@ -1164,6 +1164,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1260,6 +1261,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1357,6 +1359,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1617,6 +1620,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-5.17/wmi.h b/ath10k-5.17/wmi.h
+index ecb8fe4..8f963bd 100644
+--- a/ath10k-5.17/wmi.h
++++ b/ath10k-5.17/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-5.19/core.c b/ath10k-5.19/core.c
+index 4589f04..bc28e2c 100644
+--- a/ath10k-5.19/core.c
++++ b/ath10k-5.19/core.c
+@@ -3091,7 +3091,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-5.19/hw.c b/ath10k-5.19/hw.c
+index c0c4dbd..1df6e63 100644
+--- a/ath10k-5.19/hw.c
++++ b/ath10k-5.19/hw.c
+@@ -585,6 +585,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1161,6 +1211,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-5.19/wmi.c b/ath10k-5.19/wmi.c
+index 949205d..03d1d16 100644
+--- a/ath10k-5.19/wmi.c
++++ b/ath10k-5.19/wmi.c
+@@ -1164,6 +1164,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1260,6 +1261,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1357,6 +1359,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1617,6 +1620,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-5.19/wmi.h b/ath10k-5.19/wmi.h
+index ecb8fe4..8f963bd 100644
+--- a/ath10k-5.19/wmi.h
++++ b/ath10k-5.19/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.10/core.c b/ath10k-6.10/core.c
+index 6d1465a..1906c70 100644
+--- a/ath10k-6.10/core.c
++++ b/ath10k-6.10/core.c
+@@ -3103,7 +3103,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.10/hw.c b/ath10k-6.10/hw.c
+index 4621a60..b0b1ee5 100644
+--- a/ath10k-6.10/hw.c
++++ b/ath10k-6.10/hw.c
+@@ -586,6 +586,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1162,6 +1212,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.10/wmi.c b/ath10k-6.10/wmi.c
+index fdd8364..67b7922 100644
+--- a/ath10k-6.10/wmi.c
++++ b/ath10k-6.10/wmi.c
+@@ -1165,6 +1165,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1261,6 +1262,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1358,6 +1360,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1618,6 +1621,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.10/wmi.h b/ath10k-6.10/wmi.h
+index bdc0560..d1b6132 100644
+--- a/ath10k-6.10/wmi.h
++++ b/ath10k-6.10/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.11/core.c b/ath10k-6.11/core.c
+index c715fb6..23f4a56 100644
+--- a/ath10k-6.11/core.c
++++ b/ath10k-6.11/core.c
+@@ -3120,7 +3120,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.11/hw.c b/ath10k-6.11/hw.c
+index 4621a60..b0b1ee5 100644
+--- a/ath10k-6.11/hw.c
++++ b/ath10k-6.11/hw.c
+@@ -586,6 +586,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1162,6 +1212,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.11/wmi.c b/ath10k-6.11/wmi.c
+index c09a64d..0b3cf6a 100644
+--- a/ath10k-6.11/wmi.c
++++ b/ath10k-6.11/wmi.c
+@@ -1165,6 +1165,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1261,6 +1262,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1358,6 +1360,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1618,6 +1621,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.11/wmi.h b/ath10k-6.11/wmi.h
+index b06283d..5953fcd 100644
+--- a/ath10k-6.11/wmi.h
++++ b/ath10k-6.11/wmi.h
+@@ -3974,6 +3974,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4294,6 +4295,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.14/core.c b/ath10k-6.14/core.c
+index c715fb6..23f4a56 100644
+--- a/ath10k-6.14/core.c
++++ b/ath10k-6.14/core.c
+@@ -3120,7 +3120,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.14/hw.c b/ath10k-6.14/hw.c
+index 4621a60..b0b1ee5 100644
+--- a/ath10k-6.14/hw.c
++++ b/ath10k-6.14/hw.c
+@@ -586,6 +586,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1162,6 +1212,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.14/wmi.c b/ath10k-6.14/wmi.c
+index 1498322..fa0ad46 100644
+--- a/ath10k-6.14/wmi.c
++++ b/ath10k-6.14/wmi.c
+@@ -1165,6 +1165,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1261,6 +1262,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1358,6 +1360,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1618,6 +1621,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.14/wmi.h b/ath10k-6.14/wmi.h
+index b06283d..5953fcd 100644
+--- a/ath10k-6.14/wmi.h
++++ b/ath10k-6.14/wmi.h
+@@ -3974,6 +3974,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4294,6 +4295,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.2/core.c b/ath10k-6.2/core.c
+index 8ffce87..8c6df8a 100644
+--- a/ath10k-6.2/core.c
++++ b/ath10k-6.2/core.c
+@@ -3110,7 +3110,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.2/hw.c b/ath10k-6.2/hw.c
+index 30384b8..8c43969 100644
+--- a/ath10k-6.2/hw.c
++++ b/ath10k-6.2/hw.c
+@@ -585,6 +585,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1161,6 +1211,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.2/wmi.c b/ath10k-6.2/wmi.c
+index fd271fc..52b3667 100644
+--- a/ath10k-6.2/wmi.c
++++ b/ath10k-6.2/wmi.c
+@@ -1164,6 +1164,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1260,6 +1261,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1357,6 +1359,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1617,6 +1620,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.2/wmi.h b/ath10k-6.2/wmi.h
+index 5b6414a..941cd3b 100644
+--- a/ath10k-6.2/wmi.h
++++ b/ath10k-6.2/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.4/core.c b/ath10k-6.4/core.c
+index 8ffce87..8c6df8a 100644
+--- a/ath10k-6.4/core.c
++++ b/ath10k-6.4/core.c
+@@ -3110,7 +3110,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.4/hw.c b/ath10k-6.4/hw.c
+index 30384b8..8c43969 100644
+--- a/ath10k-6.4/hw.c
++++ b/ath10k-6.4/hw.c
+@@ -585,6 +585,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1161,6 +1211,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.4/wmi.c b/ath10k-6.4/wmi.c
+index 149bca3..fd5a018 100644
+--- a/ath10k-6.4/wmi.c
++++ b/ath10k-6.4/wmi.c
+@@ -1164,6 +1164,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1260,6 +1261,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1357,6 +1359,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1617,6 +1620,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.4/wmi.h b/ath10k-6.4/wmi.h
+index 5b6414a..941cd3b 100644
+--- a/ath10k-6.4/wmi.h
++++ b/ath10k-6.4/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.7/core.c b/ath10k-6.7/core.c
+index 1977de2..3746420 100644
+--- a/ath10k-6.7/core.c
++++ b/ath10k-6.7/core.c
+@@ -3101,7 +3101,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.7/hw.c b/ath10k-6.7/hw.c
+index 30384b8..8c43969 100644
+--- a/ath10k-6.7/hw.c
++++ b/ath10k-6.7/hw.c
+@@ -585,6 +585,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1161,6 +1211,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.7/wmi.c b/ath10k-6.7/wmi.c
+index 7bdf12f..d928c63 100644
+--- a/ath10k-6.7/wmi.c
++++ b/ath10k-6.7/wmi.c
+@@ -1164,6 +1164,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1260,6 +1261,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1357,6 +1359,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1617,6 +1620,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.7/wmi.h b/ath10k-6.7/wmi.h
+index 7fa5a5e..ef867ed 100644
+--- a/ath10k-6.7/wmi.h
++++ b/ath10k-6.7/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+diff --git a/ath10k-6.9/core.c b/ath10k-6.9/core.c
+index 17b6a64..0ede88f 100644
+--- a/ath10k-6.9/core.c
++++ b/ath10k-6.9/core.c
+@@ -3121,7 +3121,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work)
+                                        set_coverage_class_work);
+       if (ar->hw_params.hw_ops->set_coverage_class)
+-              ar->hw_params.hw_ops->set_coverage_class(ar, -1);
++              ar->hw_params.hw_ops->set_coverage_class(ar, ar->fw_coverage.coverage_class);
+ }
+ static int ath10k_core_init_firmware_features(struct ath10k *ar)
+diff --git a/ath10k-6.9/hw.c b/ath10k-6.9/hw.c
+index 4621a60..b0b1ee5 100644
+--- a/ath10k-6.9/hw.c
++++ b/ath10k-6.9/hw.c
+@@ -586,6 +586,56 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
+       survey->time_busy = CCNT_TO_MSEC(ar, rcc);
+ }
++/* Wireless firmware version 10.4 supports setting Coverage Class by
+++ * setting via wmi tx_ack_timeout; chipsets a.o. ipq40xx, qca99xx
+++*/
++static void ath10k_hw_qca99xx_set_coverage_class(struct ath10k *ar,
++                                               s16 value)
++{
++      u32 timeout;
++      int ret;
++
++      mutex_lock(&ar->conf_mutex);
++
++      /* Only execute if the core is started. */
++      if ((ar->state != ATH10K_STATE_ON) &&
++          (ar->state != ATH10K_STATE_RESTARTED)) {
++              ath10k_warn(ar, "ath10k core not yet started");
++              goto unlock;
++      }
++
++      if (value < 0)
++              value = ar->fw_coverage.coverage_class;
++
++      /* WIP: it appears that one wmi timeout unit accounts for 3 C.Class units
++      * so we need an integer divide by three; feedback welcome for distances
++      * 10-20 km; tested at 7 km */
++      if (value > 0) {
++              timeout = 0x40 + (value / 3) + 0x01;
++
++              /* Limit to 0xff as the destination register is u8 */
++              if (timeout > 0xff)
++                      timeout = 0xff;
++      }
++      else
++              timeout = 0x40;
++
++      /* set Coverage Class via wmi */
++      ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_ack_timeout,
++              timeout);
++      if (ret) {
++              ath10k_warn(ar, "failed to set tx-acktimeout: %d, timeout: 0x%x\n"
++                      , ret, timeout);
++      }
++
++unlock:
++      spin_lock_bh(&ar->data_lock);
++      ar->fw_coverage.coverage_class = value;
++      spin_unlock_bh(&ar->data_lock);
++
++      mutex_unlock(&ar->conf_mutex);
++}
++
+ /* The stock firmware does not support setting the coverage class. Instead this
+  * function monitors and modifies the corresponding MAC registers.
+  */
+@@ -1162,6 +1212,7 @@ const struct ath10k_hw_ops qca988x_ops = {
+ };
+ const struct ath10k_hw_ops qca99x0_ops = {
++      .set_coverage_class = ath10k_hw_qca99xx_set_coverage_class,
+       .is_rssi_enable = ath10k_htt_tx_rssi_enable,
+ };
+diff --git a/ath10k-6.9/wmi.c b/ath10k-6.9/wmi.c
+index fdd8364..67b7922 100644
+--- a/ath10k-6.9/wmi.c
++++ b/ath10k-6.9/wmi.c
+@@ -1165,6 +1165,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -1261,6 +1262,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+@@ -1358,6 +1360,7 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+       .arp_srcaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arp_dstaddr = WMI_PDEV_PARAM_UNSUPPORTED,
+       .enable_btcoex = WMI_PDEV_PARAM_UNSUPPORTED,
++      .tx_ack_timeout = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+ /* firmware 10.2 specific mappings */
+@@ -1618,6 +1621,7 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
+       .arp_srcaddr = WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
+       .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+       .enable_btcoex = WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      .tx_ack_timeout = WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT,
+ };
+ static const u8 wmi_key_cipher_suites[] = {
+diff --git a/ath10k-6.9/wmi.h b/ath10k-6.9/wmi.h
+index bdc0560..d1b6132 100644
+--- a/ath10k-6.9/wmi.h
++++ b/ath10k-6.9/wmi.h
+@@ -3939,6 +3939,7 @@ struct wmi_pdev_param_map {
+       u32 rfkill_config;
+       u32 rfkill_enable;
+       u32 peer_stats_info_enable;
++      u32 tx_ack_timeout;
+ };
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+@@ -4259,6 +4260,8 @@ enum wmi_10_4_pdev_param {
+       WMI_10_4_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+       WMI_10_4_PDEV_PARAM_ATF_SSID_GROUP_POLICY,
+       WMI_10_4_PDEV_PARAM_ENABLE_BTCOEX,
++      /* TX acknowledge timeout. Advised range: 0x40 - 0xFF microsec. */
++      WMI_10_4_PDEV_PARAM_TX_ACK_TIMEOUT = 0x68,
+ };
+ struct wmi_pdev_set_param_cmd {
+-- 
+2.43.0
+