diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cde2d9b..e0b6aff 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -12,6 +12,7 @@
 #include "debug.h"
 #include "trace.h"
 #include "mac.h"
+#include "wmi-ops.h"
 
 #include <linux/log2.h>
 #include <linux/bitfield.h>
@@ -3310,21 +3311,6 @@ void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 		dev_kfree_skb_any(skb);
 }
 
-static inline s8 ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate)
-{
-	static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12,
-					  18, 24, 36, 48, 54};
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(legacy_rates); i++) {
-		if (rate == legacy_rates[i])
-			return i;
-	}
-
-	ath10k_warn(ar, "Invalid legacy rate %hhd peer stats", rate);
-	return -EINVAL;
-}
-
 static void
 ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
 				    struct ath10k_sta *arsta,
@@ -3454,10 +3440,12 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
 {
 	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
 	struct ieee80211_chanctx_conf *conf = NULL;
-	u8 rate = 0, sgi;
-	s8 rate_idx = 0;
+	u8 sgi;
+	s8 rate_idx = -1;
 	bool skip_auto_rate;
 	struct rate_info txrate;
+	enum nl80211_band band;
+	struct ieee80211_supported_band *sband;
 
 	lockdep_assert_held(&ar->data_lock);
 
@@ -3475,7 +3463,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
 		return;
 
 	if (txrate.flags == WMI_RATE_PREAMBLE_VHT && txrate.mcs > 9) {
-		ath10k_warn(ar, "Invalid VHT mcs %hhd peer stats",  txrate.mcs);
+		ath10k_warn(ar, "Invalid VHT mcs %hhd peer stats", txrate.mcs);
 		return;
 	}
 
@@ -3490,14 +3478,19 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
 	memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status));
 	if (txrate.flags == WMI_RATE_PREAMBLE_CCK ||
 	    txrate.flags == WMI_RATE_PREAMBLE_OFDM) {
-		rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode);
-		/* This is hacky, FW sends CCK rate 5.5Mbps as 6 */
-		if (rate == 6 && txrate.flags == WMI_RATE_PREAMBLE_CCK)
-			rate = 5;
-		rate_idx = ath10k_get_legacy_rate_idx(ar, rate);
-		if (rate_idx < 0)
+		if (!arsta->arvif || !arsta->arvif->vif)
+			return;
+
+		conf = rcu_dereference(arsta->arvif->vif->chanctx_conf);
+		if (!conf)
+			return;
+
+		band = conf->def.chan->band;
+		sband = &ar->mac.sbands[band];
+		if (ath10k_wmi_get_legacy_rate(ar, sband, peer_stats->ratecode,
+					       &arsta->txrate.legacy,
+					       &rate_idx))
 			return;
-		arsta->txrate.legacy = rate;
 	} else if (txrate.flags == WMI_RATE_PREAMBLE_HT) {
 		arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
 		arsta->txrate.mcs = txrate.mcs + 8 * (txrate.nss - 1);
@@ -3508,10 +3501,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
 
 	switch (txrate.flags) {
 	case WMI_RATE_PREAMBLE_OFDM:
-		if (arsta->arvif && arsta->arvif->vif)
-			conf = rcu_dereference(arsta->arvif->vif->chanctx_conf);
-		if (conf && conf->def.chan->band == NL80211_BAND_5GHZ)
-			arsta->tx_info.status.rates[0].idx = rate_idx - 4;
+		arsta->tx_info.status.rates[0].idx = rate_idx;
 		break;
 	case WMI_RATE_PREAMBLE_CCK:
 		arsta->tx_info.status.rates[0].idx = rate_idx;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 5e3450c..555f850 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -94,19 +94,6 @@ static struct ieee80211_rate ath10k_rates_rev2[] = {
 
 #define ath10k_wmi_legacy_rates ath10k_rates
 
-static bool ath10k_mac_bitrate_is_cck(int bitrate)
-{
-	switch (bitrate) {
-	case 10:
-	case 20:
-	case 55:
-	case 110:
-		return true;
-	}
-
-	return false;
-}
-
 static u8 ath10k_mac_bitrate_to_rate(int bitrate)
 {
 	return DIV_ROUND_UP(bitrate, 5) |
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 98d83a2..ce700ed 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -92,4 +92,15 @@ static inline void ath10k_tx_h_seq_no(struct ieee80211_vif *vif,
 	}
 }
 
+static inline bool ath10k_mac_bitrate_is_cck(int bitrate)
+{
+	switch (bitrate) {
+	case 10:
+	case 20:
+	case 55:
+	case 110:
+		return true;
+	}
+	return false;
+}
 #endif /* _MAC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index 1491c25..aa20e1c 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -216,7 +216,9 @@ struct wmi_ops {
 	struct sk_buff *(*gen_bb_timing)
 			(struct ath10k *ar,
 			 const struct wmi_bb_timing_cfg_arg *arg);
-
+	int (*get_legacy_rate)(struct ath10k *ar,
+			       struct ieee80211_supported_band *sband,
+			       u8 ratecode, u16 *rate, s8 *idx);
 };
 
 int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
@@ -1616,4 +1618,15 @@ ath10k_wmi_pdev_bb_timing(struct ath10k *ar,
 	return ath10k_wmi_cmd_send(ar, skb,
 				   ar->wmi.cmd->set_bb_timing_cmdid);
 }
+
+static inline int
+ath10k_wmi_get_legacy_rate(struct ath10k *ar,
+			   struct ieee80211_supported_band *sband,
+			   u8 ratecode, u16 *rate, s8 *idx)
+{
+	if (!ar->wmi.ops->get_legacy_rate)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->get_legacy_rate(ar, sband, ratecode, rate, idx);
+}
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index ae12a58..8964fa8 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -4009,6 +4009,43 @@ ath10k_wmi_tlv_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id,
 	return skb;
 }
 
+static int
+ath10k_wmi_tlv_op_get_legacy_rate(struct ath10k *ar,
+				  struct ieee80211_supported_band *sband,
+				  u8 ratecode, u16 *rate, s8 *idx)
+{
+	u8 hw_rate = ATH10K_HW_LEGACY_RATE(ratecode);
+	u8 preamble = ATH10K_HW_PREAMBLE(ratecode);
+	int i;
+	const struct ieee80211_rate *bitrates;
+	bool cck;
+
+	if (!sband || !sband->bitrates || !rate || !idx)
+		return -EINVAL;
+
+	if (WARN_ON_ONCE(sband->n_bitrates > S8_MAX))
+		return -EINVAL;
+
+	cck = (preamble == WMI_RATE_PREAMBLE_CCK);
+	for (i = 0; i < sband->n_bitrates; i++) {
+		bitrates = &sband->bitrates[i];
+
+		if (ath10k_mac_bitrate_is_cck(bitrates->bitrate) != cck)
+			continue;
+
+		if (bitrates->hw_value == hw_rate ||
+		    (bitrates->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
+		     bitrates->hw_value_short == hw_rate)) {
+			*rate = bitrates->bitrate;
+			*idx = i;
+			return 0;
+		}
+	}
+
+	ath10k_warn(ar, "Invalid legacy ratecode %hhd peer stats", ratecode);
+	return -EINVAL;
+}
+
 /****************/
 /* TLV mappings */
 /****************/
@@ -4445,6 +4482,7 @@ static const struct wmi_ops wmi_tlv_ops = {
 	.gen_echo = ath10k_wmi_tlv_op_gen_echo,
 	.gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf,
 	.gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable,
+	.get_legacy_rate = ath10k_wmi_tlv_op_get_legacy_rate,
 };
 
 static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = {
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 9f564e2..304e09f 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -9016,6 +9016,47 @@ ath10k_wmi_10_2_4_op_gen_bb_timing(struct ath10k *ar,
 	return skb;
 }
 
+static int
+ath10k_wmi_op_get_legacy_rate(struct ath10k *ar,
+			      struct ieee80211_supported_band *sband,
+			      u8 ratecode, u16 *rate, s8 *idx)
+{
+	u16 hw_rate = ATH10K_HW_LEGACY_RATE(ratecode);
+	u8 preamble = ATH10K_HW_PREAMBLE(ratecode);
+	int i;
+	const struct ieee80211_rate *bitrates;
+	bool cck;
+
+	if (!sband || !sband->bitrates || !rate || !idx)
+		return -EINVAL;
+
+	if (WARN_ON_ONCE(sband->n_bitrates > S8_MAX))
+		return -EINVAL;
+
+	cck = (preamble == WMI_RATE_PREAMBLE_CCK);
+
+	/* From Mbps to 100kbps */
+	hw_rate *= 10;
+
+	/* This is hacky, FW sends CCK rate 5.5Mbps as 6 */
+	if (cck && hw_rate == 60)
+		hw_rate = 55;
+
+	for (i = 0; i < sband->n_bitrates; i++) {
+		bitrates = &sband->bitrates[i];
+		if (ath10k_mac_bitrate_is_cck(bitrates->bitrate) != cck)
+			continue;
+
+		if (bitrates->bitrate == hw_rate) {
+			*rate = bitrates->bitrate;
+			*idx = i;
+			return 0;
+		}
+	}
+	ath10k_warn(ar, "Invalid legacy ratecode %hhd peer stats", ratecode);
+	return -EINVAL;
+}
+
 static const struct wmi_ops wmi_ops = {
 	.rx = ath10k_wmi_op_rx,
 	.map_svc = wmi_main_svc_map,
@@ -9083,6 +9124,7 @@ static const struct wmi_ops wmi_ops = {
 	/* .gen_p2p_go_bcn_ie not implemented */
 	/* .gen_adaptive_qcs not implemented */
 	/* .gen_pdev_enable_adaptive_cca not implemented */
+	.get_legacy_rate = ath10k_wmi_op_get_legacy_rate,
 };
 
 static const struct wmi_ops wmi_10_1_ops = {
@@ -9153,6 +9195,7 @@ static const struct wmi_ops wmi_10_1_ops = {
 	/* .gen_p2p_go_bcn_ie not implemented */
 	/* .gen_adaptive_qcs not implemented */
 	/* .gen_pdev_enable_adaptive_cca not implemented */
+	.get_legacy_rate = ath10k_wmi_op_get_legacy_rate,
 };
 
 static const struct wmi_ops wmi_10_2_ops = {
@@ -9221,6 +9264,7 @@ static const struct wmi_ops wmi_10_2_ops = {
 	.fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
 	.get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
 	/* .gen_pdev_enable_adaptive_cca not implemented */
+	.get_legacy_rate = ath10k_wmi_op_get_legacy_rate,
 };
 
 static const struct wmi_ops wmi_10_2_4_ops = {
@@ -9295,6 +9339,7 @@ static const struct wmi_ops wmi_10_2_4_ops = {
 	/* .gen_prb_tmpl not implemented */
 	/* .gen_p2p_go_bcn_ie not implemented */
 	/* .gen_adaptive_qcs not implemented */
+	.get_legacy_rate = ath10k_wmi_op_get_legacy_rate,
 };
 
 static const struct wmi_ops wmi_10_4_ops = {
@@ -9371,6 +9416,7 @@ static const struct wmi_ops wmi_10_4_ops = {
 	.gen_pdev_bss_chan_info_req = ath10k_wmi_10_2_op_gen_pdev_bss_chan_info,
 	.gen_echo = ath10k_wmi_op_gen_echo,
 	.gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config,
+	.get_legacy_rate = ath10k_wmi_op_get_legacy_rate,
 };
 
 int ath10k_wmi_attach(struct ath10k *ar)
