peer->removed = true;
 }
 
+/* HT MCS parameters with Nss = 1 */
+static const struct ath10k_index_ht_data_rate_type supported_ht_mcs_rate_nss1[] = {
+       /* MCS  L20   L40   S20  S40 */
+       {0,  { 65,  135,  72,  150} },
+       {1,  { 130, 270,  144, 300} },
+       {2,  { 195, 405,  217, 450} },
+       {3,  { 260, 540,  289, 600} },
+       {4,  { 390, 810,  433, 900} },
+       {5,  { 520, 1080, 578, 1200} },
+       {6,  { 585, 1215, 650, 1350} },
+       {7,  { 650, 1350, 722, 1500} }
+};
+
+/* HT MCS parameters with Nss = 2 */
+static const struct ath10k_index_ht_data_rate_type supported_ht_mcs_rate_nss2[] = {
+       /* MCS  L20    L40   S20   S40 */
+       {0,  {130,  270,  144,  300} },
+       {1,  {260,  540,  289,  600} },
+       {2,  {390,  810,  433,  900} },
+       {3,  {520,  1080, 578,  1200} },
+       {4,  {780,  1620, 867,  1800} },
+       {5,  {1040, 2160, 1156, 2400} },
+       {6,  {1170, 2430, 1300, 2700} },
+       {7,  {1300, 2700, 1444, 3000} }
+};
+
+/* MCS parameters with Nss = 1 */
+static const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = {
+       /* MCS  L80    S80     L40   S40    L20   S20 */
+       {0,  {293,  325},  {135,  150},  {65,   72} },
+       {1,  {585,  650},  {270,  300},  {130,  144} },
+       {2,  {878,  975},  {405,  450},  {195,  217} },
+       {3,  {1170, 1300}, {540,  600},  {260,  289} },
+       {4,  {1755, 1950}, {810,  900},  {390,  433} },
+       {5,  {2340, 2600}, {1080, 1200}, {520,  578} },
+       {6,  {2633, 2925}, {1215, 1350}, {585,  650} },
+       {7,  {2925, 3250}, {1350, 1500}, {650,  722} },
+       {8,  {3510, 3900}, {1620, 1800}, {780,  867} },
+       {9,  {3900, 4333}, {1800, 2000}, {780,  867} }
+};
+
+/*MCS parameters with Nss = 2 */
+static const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = {
+       /* MCS  L80    S80     L40   S40    L20   S20 */
+       {0,  {585,  650},  {270,  300},  {130,  144} },
+       {1,  {1170, 1300}, {540,  600},  {260,  289} },
+       {2,  {1755, 1950}, {810,  900},  {390,  433} },
+       {3,  {2340, 2600}, {1080, 1200}, {520,  578} },
+       {4,  {3510, 3900}, {1620, 1800}, {780,  867} },
+       {5,  {4680, 5200}, {2160, 2400}, {1040, 1156} },
+       {6,  {5265, 5850}, {2430, 2700}, {1170, 1300} },
+       {7,  {5850, 6500}, {2700, 3000}, {1300, 1444} },
+       {8,  {7020, 7800}, {3240, 3600}, {1560, 1733} },
+       {9,  {7800, 8667}, {3600, 4000}, {1560, 1733} }
+};
+
+static void ath10k_mac_get_rate_flags_ht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs,
+                                        u8 *flags, u8 *bw)
+{
+       struct ath10k_index_ht_data_rate_type *mcs_rate;
+
+       mcs_rate = (struct ath10k_index_ht_data_rate_type *)
+                  ((nss == 1) ? &supported_ht_mcs_rate_nss1 :
+                  &supported_ht_mcs_rate_nss2);
+
+       if (rate == mcs_rate[mcs].supported_rate[0]) {
+               *bw = RATE_INFO_BW_20;
+       } else if (rate == mcs_rate[mcs].supported_rate[1]) {
+               *bw |= RATE_INFO_BW_40;
+       } else if (rate == mcs_rate[mcs].supported_rate[2]) {
+               *bw |= RATE_INFO_BW_20;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else if (rate == mcs_rate[mcs].supported_rate[3]) {
+               *bw |= RATE_INFO_BW_40;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else {
+               ath10k_warn(ar, "invalid ht params rate %d 100kbps nss %d mcs %d",
+                           rate, nss, mcs);
+       }
+}
+
+static void ath10k_mac_get_rate_flags_vht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs,
+                                         u8 *flags, u8 *bw)
+{
+       struct ath10k_index_vht_data_rate_type *mcs_rate;
+
+       mcs_rate = (struct ath10k_index_vht_data_rate_type *)
+                  ((nss == 1) ? &supported_vht_mcs_rate_nss1 :
+                  &supported_vht_mcs_rate_nss2);
+
+       if (rate == mcs_rate[mcs].supported_VHT80_rate[0]) {
+               *bw = RATE_INFO_BW_80;
+       } else if (rate == mcs_rate[mcs].supported_VHT80_rate[1]) {
+               *bw = RATE_INFO_BW_80;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else if (rate == mcs_rate[mcs].supported_VHT40_rate[0]) {
+               *bw = RATE_INFO_BW_40;
+       } else if (rate == mcs_rate[mcs].supported_VHT40_rate[1]) {
+               *bw = RATE_INFO_BW_40;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else if (rate == mcs_rate[mcs].supported_VHT20_rate[0]) {
+               *bw = RATE_INFO_BW_20;
+       } else if (rate == mcs_rate[mcs].supported_VHT20_rate[1]) {
+               *bw = RATE_INFO_BW_20;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else {
+               ath10k_warn(ar, "invalid vht params rate %d 100kbps nss %d mcs %d",
+                           rate, nss, mcs);
+       }
+}
+
+static void ath10k_mac_get_rate_flags(struct ath10k *ar, u32 rate,
+                                     enum ath10k_phy_mode mode, u8 nss, u8 mcs,
+                                     u8 *flags, u8 *bw)
+{
+       if (mode == ATH10K_PHY_MODE_HT) {
+               *flags = RATE_INFO_FLAGS_MCS;
+               ath10k_mac_get_rate_flags_ht(ar, rate, nss, mcs, flags, bw);
+       } else if (mode == ATH10K_PHY_MODE_VHT) {
+               *flags = RATE_INFO_FLAGS_VHT_MCS;
+               ath10k_mac_get_rate_flags_vht(ar, rate, nss, mcs, flags, bw);
+       }
+}
+
+static void ath10k_mac_parse_bitrate(struct ath10k *ar, u32 rate_code,
+                                    u32 bitrate_kbps, struct rate_info *rate)
+{
+       enum ath10k_phy_mode mode = ATH10K_PHY_MODE_LEGACY;
+       enum wmi_rate_preamble preamble = WMI_TLV_GET_HW_RC_PREAM_V1(rate_code);
+       u8 nss = WMI_TLV_GET_HW_RC_NSS_V1(rate_code) + 1;
+       u8 mcs = WMI_TLV_GET_HW_RC_RATE_V1(rate_code);
+       u8 flags = 0, bw = 0;
+
+       if (preamble == WMI_RATE_PREAMBLE_HT)
+               mode = ATH10K_PHY_MODE_HT;
+       else if (preamble == WMI_RATE_PREAMBLE_VHT)
+               mode = ATH10K_PHY_MODE_VHT;
+
+       ath10k_mac_get_rate_flags(ar, bitrate_kbps / 100, mode, nss, mcs, &flags, &bw);
+
+       ath10k_dbg(ar, ATH10K_DBG_MAC,
+                  "mac parse bitrate preamble %d mode %d nss %d mcs %d flags %x bw %d\n",
+                  preamble, mode, nss, mcs, flags, bw);
+
+       rate->flags = flags;
+       rate->bw = bw;
+       rate->legacy = bitrate_kbps / 100;
+       rate->nss = nss;
+       rate->mcs = mcs;
+}
+
 static void ath10k_mac_sta_get_peer_stats_info(struct ath10k *ar,
                                               struct ieee80211_sta *sta,
                                               struct station_info *sinfo)
                ath10k_warn(ar, "timed out waiting peer stats info\n");
                return;
        }
+
+       if (arsta->rx_rate_code != 0 && arsta->rx_bitrate_kbps != 0) {
+               ath10k_mac_parse_bitrate(ar, arsta->rx_rate_code,
+                                        arsta->rx_bitrate_kbps,
+                                        &sinfo->rxrate);
+
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+               arsta->rx_rate_code = 0;
+               arsta->rx_bitrate_kbps = 0;
+       }
 }
 
 static void ath10k_sta_statistics(struct ieee80211_hw *hw,