diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 74cc55c1bf28edc3c037acd1de46190b8d9c0d81..db961a59247f0b175a5c16a4000029d62a6bca7d 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -1638,12 +1638,20 @@ struct nl80211_sta_flag_update {
  *
  * These attribute types are used with %NL80211_STA_INFO_TXRATE
  * when getting information about the bitrate of a station.
+ * There are 2 attributes for bitrate, a legacy one that represents
+ * a 16-bit value, and new one that represents a 32-bit value.
+ * If the rate value fits into 16 bit, both attributes are reported
+ * with the same value. If the rate is too high to fit into 16 bits
+ * (>6.5535Gbps) only 32-bit attribute is included.
+ * User space tools encouraged to use the 32-bit attribute and fall
+ * back to the 16-bit one for compatibility with older kernels.
  *
  * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
  * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
  * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
  * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
  * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
@@ -1653,6 +1661,7 @@ enum nl80211_rate_info {
 	NL80211_RATE_INFO_MCS,
 	NL80211_RATE_INFO_40_MHZ_WIDTH,
 	NL80211_RATE_INFO_SHORT_GI,
+	NL80211_RATE_INFO_BITRATE32,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0b564e83a24ba1b5205cddbd9b7be79e69b729c7..8837efc368f9702bd160b807488465687fe92d92 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3487,7 +3487,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
  *
  * return 0 if MCS index >= 32
  */
-u16 cfg80211_calculate_bitrate(struct rate_info *rate);
+u32 cfg80211_calculate_bitrate(struct rate_info *rate);
 
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 77102e66f1eac1cc2e38cb00e429e96580b7acc7..2a5cdb60bc6e0ba15003592149bb10d56d4d0c4d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2618,7 +2618,8 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
 				 int attr)
 {
 	struct nlattr *rate;
-	u16 bitrate;
+	u32 bitrate;
+	u16 bitrate_compat;
 
 	rate = nla_nest_start(msg, attr);
 	if (!rate)
@@ -2626,8 +2627,12 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
 
 	/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
 	bitrate = cfg80211_calculate_bitrate(info);
+	/* report 16-bit bitrate only if we can */
+	bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
 	if ((bitrate > 0 &&
-	     nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate)) ||
+	     nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) ||
+	    (bitrate_compat > 0 &&
+	     nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) ||
 	    ((info->flags & RATE_INFO_FLAGS_MCS) &&
 	     nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
 	    ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 0228c64e73d84a37a545d401d1bf5bb8e8b1f79c..6e52726f7fe36b510110745d8329fcc2e2e3b9d9 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -900,7 +900,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
 	return err;
 }
 
-u16 cfg80211_calculate_bitrate(struct rate_info *rate)
+u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 {
 	int modulation, streams, bitrate;