diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index c47f43e65a2f..884b71a96084 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1692,6 +1692,21 @@ enum ieee80211_vht_chanwidth {
 	IEEE80211_VHT_CHANWIDTH_80P80MHZ	= 3,
 };
 
+/**
+ * enum ieee80211_center_freq_seg1_location - the location of center
+ * frequency segment1
+ * @IEEE80211_CENTER_FREQ_SEG1_NONE: center freq seg1 is located no where
+ * @IEEE80211_CENTER_FREQ_SEG1_VHT_OPER: center freq seg1 is located in VHT
+ * operation IE
+ * @IEEE80211_CENTER_FREQ_SEG1_HT_OPER: center freq seg1 is located in HT
+ * operation IE
+ */
+enum ieee80211_center_freq_seg1_location {
+	IEEE80211_CENTER_FREQ_SEG1_NONE         = 0,
+	IEEE80211_CENTER_FREQ_SEG1_VHT_OPER     = 1,
+	IEEE80211_CENTER_FREQ_SEG1_HT_OPER      = 2,
+};
+
 /**
  * struct ieee80211_vht_operation - VHT operation IE
  *
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index ae1cb2c68722..6600e78e2bde 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -19,6 +19,78 @@
 #include "sta_info.h"
 #include "wme.h"
 
+enum ieee80211_vht_chanwidth ieee80211_vht_get_actual_chwidth(u8 vht_oper_bw,
+							      u32 seg0,
+							      u32 seg1)
+{
+	enum ieee80211_vht_chanwidth ret = IEEE80211_VHT_CHANWIDTH_USE_HT;
+
+	if (vht_oper_bw != IEEE80211_VHT_CHANWIDTH_80MHZ)
+		return vht_oper_bw;
+
+	if (!seg1) {
+		return IEEE80211_VHT_CHANWIDTH_80MHZ;
+	} else {
+		int diff;
+
+		diff = abs((int) seg0 - (int) seg1);
+
+		if (diff == 8)
+			return IEEE80211_VHT_CHANWIDTH_160MHZ;
+
+		if (diff > 16)
+			return IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+	}
+
+	return ret;
+}
+
+enum ieee80211_center_freq_seg1_location
+ieee80211_get_center_freq_seg1_location(struct ieee80211_sub_if_data *sdata,
+					u32 vht_cap_info,
+					u8 actual_chanwidth)
+{
+	u8 ext_nss_bw_supp = (vht_cap_info &
+			      IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) >>
+			      IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT;
+	u8 supp_chwidth = (vht_cap_info &
+			   IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) >>
+			   IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_SHIFT;
+	enum ieee80211_center_freq_seg1_location res =
+					IEEE80211_CENTER_FREQ_SEG1_NONE;
+
+	/* The bandwidth is less than 80+80/160MHz */
+	if (actual_chanwidth < IEEE80211_VHT_CHANWIDTH_160MHZ)
+		return IEEE80211_CENTER_FREQ_SEG1_NONE;
+
+	switch (supp_chwidth) {
+	case 0:
+		if ((ext_nss_bw_supp > 1) ||
+		    ((ext_nss_bw_supp == 1) &&
+		     (actual_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ)))
+			res = IEEE80211_CENTER_FREQ_SEG1_HT_OPER; /* CCFS2 */
+		break;
+	case 1:
+		if ((ext_nss_bw_supp > 2) ||
+		    (actual_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ))
+			res = IEEE80211_CENTER_FREQ_SEG1_VHT_OPER; /* CCFS1 */
+		else if (ext_nss_bw_supp > 0)
+			res = IEEE80211_CENTER_FREQ_SEG1_HT_OPER; /* CCFS2 */
+		break;
+	case 2:
+		res = IEEE80211_CENTER_FREQ_SEG1_VHT_OPER;
+		if ((ext_nss_bw_supp > 0) && (ext_nss_bw_supp < 3))
+			sdata_info(sdata,
+				   "Invalid ext_nss_bw_supp %u for chwidth 2",
+				   ext_nss_bw_supp);
+		break;
+	default:
+		break;
+	}
+
+	return res;
+}
+
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 				 struct ieee802_11_elems *elems,
 				 enum nl80211_band current_band,
@@ -133,17 +205,30 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 	}
 
 	if (wide_bw_chansw_ie) {
+		u8 new_channel_width = wide_bw_chansw_ie->new_channel_width;
+		u8 new_seg0 = wide_bw_chansw_ie->new_center_freq_seg0;
+		u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1;
+		enum ieee80211_vht_chanwidth act_chanwidth =
+			ieee80211_vht_get_actual_chwidth(
+			 new_channel_width, new_seg0, new_seg1);
+		enum ieee80211_center_freq_seg1_location seg1_location =
+			ieee80211_get_center_freq_seg1_location(sdata,
+								vht_cap_info,
+								act_chanwidth);
 		struct ieee80211_vht_operation vht_oper = {
-			.chan_width =
-				wide_bw_chansw_ie->new_channel_width,
-			.center_freq_seg0_idx =
-				wide_bw_chansw_ie->new_center_freq_seg0,
-			.center_freq_seg1_idx =
-				wide_bw_chansw_ie->new_center_freq_seg1,
+			.chan_width = new_channel_width,
+			.center_freq_seg0_idx = new_seg0,
 			/* .basic_mcs_set doesn't matter */
 		};
 		struct ieee80211_ht_operation ht_oper = {};
 
+		if (seg1_location == IEEE80211_CENTER_FREQ_SEG1_VHT_OPER)
+			vht_oper.center_freq_seg1_idx = new_seg1;
+		else if (seg1_location == IEEE80211_CENTER_FREQ_SEG1_HT_OPER)
+			ht_oper.operation_mode =
+					(le16_to_cpu(new_seg1) <<
+					 IEEE80211_HT_OP_MODE_CCFS2_SHIFT);
+
 		/* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT,
 		 * to the previously parsed chandef
 		 */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5940acbe761c..9644d00cfd2f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2889,6 +2889,21 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 		chandef->edmg.channels = 0;
 	}
 
+	if (chandef->width == NL80211_CHAN_WIDTH_80) {
+		if (chandef->center_freq2) {
+			int diff = abs(chandef->center_freq1 -
+				       chandef->center_freq2);
+
+			if (diff == 40)
+				chandef->width = NL80211_CHAN_WIDTH_160;
+			else if (diff > 80)
+				chandef->width = NL80211_CHAN_WIDTH_80P80;
+
+			chandef->center_freq1 = chandef->center_freq2;
+			chandef->center_freq2 = 0;
+		}
+	}
+
 	if (!cfg80211_chandef_valid(chandef)) {
 		NL_SET_ERR_MSG(extack, "invalid channel definition");
 		return -EINVAL;
