Merge "CHROMIUM: iwl7000: Merge "core64_sp-50" driver updates" into chromeos-4.4
diff --git a/drivers/net/wireless/iwl7000/hdrs/config.h b/drivers/net/wireless/iwl7000/hdrs/config.h
index 007e24f..063aa0e 100644
--- a/drivers/net/wireless/iwl7000/hdrs/config.h
+++ b/drivers/net/wireless/iwl7000/hdrs/config.h
@@ -73,5 +73,6 @@
 #endif
 
 #define CPTCFG_IWLWIFI_DHC 1
+#define CPTCFG_IWLWIFI_DONT_DUMP_FIFOS 1
 
 #endif
diff --git a/drivers/net/wireless/iwl7000/hdrs/version.h b/drivers/net/wireless/iwl7000/hdrs/version.h
index e737f0a..cf64296 100644
--- a/drivers/net/wireless/iwl7000/hdrs/version.h
+++ b/drivers/net/wireless/iwl7000/hdrs/version.h
@@ -1,6 +1,6 @@
 #ifndef __IWL_CHROME_VERSION_H
 #define __IWL_CHROME_VERSION_H
 
-#define BACKPORTS_GIT_TRACKED "chromium:" UTS_RELEASE ":core64-70:"
+#define BACKPORTS_GIT_TRACKED "chromium:" UTS_RELEASE ":core64_sp-50:"
 
 #endif /* __IWL_CHROME_VERSION_H */
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/iwl7000/iwlwifi/fw/api/dbg-tlv.h
index fd88d3a0..3988f5f 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/fw/api/dbg-tlv.h
+++ b/drivers/net/wireless/iwl7000/iwlwifi/fw/api/dbg-tlv.h
@@ -7,6 +7,7 @@
 
 #include <linux/bitops.h>
 
+#define IWL_FW_INI_HW_SMEM_REGION_ID		15
 #define IWL_FW_INI_MAX_REGION_ID		64
 #define IWL_FW_INI_MAX_NAME			32
 #define IWL_FW_INI_MAX_CFG_NAME			64
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/fw/dbg.c b/drivers/net/wireless/iwl7000/iwlwifi/fw/dbg.c
index 155c01a..225368a 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/fw/dbg.c
@@ -804,8 +804,10 @@
 
 	/* reading RXF/TXF sizes */
 	if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
-		fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
-		fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
+		if (!IS_ENABLED(CPTCFG_IWLWIFI_DONT_DUMP_FIFOS)) {
+			fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
+			fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
+		}
 
 		/* Make room for PRPH registers */
 		if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
@@ -1165,6 +1167,13 @@
 	iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
 				 le32_to_cpu(reg->dev_addr.size));
 
+	if ((le32_to_cpu(reg->id) & IWL_FW_INI_REGION_V2_MASK) ==
+		IWL_FW_INI_HW_SMEM_REGION_ID &&
+	    fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
+		fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
+					     range->data,
+					     le32_to_cpu(reg->dev_addr.size));
+
 	return sizeof(*range) + le32_to_cpu(range->range_data_size);
 }
 
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/iwl7000/iwlwifi/iwl-dbg-tlv.c
index 3da1cd8..4af8010 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/iwl-dbg-tlv.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/iwl-dbg-tlv.c
@@ -211,6 +211,14 @@
 		return -EOPNOTSUPP;
 	}
 
+#ifdef CPTCFG_IWLWIFI_DONT_DUMP_FIFOS
+	if (type == IWL_FW_INI_REGION_DEVICE_MEMORY &&
+	    id == IWL_FW_INI_HW_SMEM_REGION_ID) {
+		IWL_DEBUG_FW(trans, "WRT: skipping HW-SMEM region\n");
+		return -EOPNOTSUPP;
+	}
+#endif
+
 	active_reg = &trans->dbg.active_regions[id];
 	if (*active_reg) {
 		IWL_WARN(trans, "WRT: Overriding region id %u\n", id);
@@ -222,6 +230,14 @@
 	if (!*active_reg)
 		return -ENOMEM;
 
+#ifdef CPTCFG_IWLWIFI_DONT_DUMP_FIFOS
+	if (type == IWL_FW_INI_REGION_TXF || type == IWL_FW_INI_REGION_RXF) {
+		struct iwl_fw_ini_region_tlv *reg = (void *)(*active_reg)->data;
+
+		reg->fifos.hdr_only = cpu_to_le32(1);
+	}
+#endif
+
 	IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type);
 
 	return 0;
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwl7000/iwlwifi/iwl-trans.h
index 7220893..f6af1c6 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwl7000/iwlwifi/iwl-trans.h
@@ -307,6 +307,8 @@
  * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
  * @STATUS_TA_ACTIVE: target access is in progress
  * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
+ * @STATUS_SUPPRESS_CMD_ERROR_ONCE: suppress "FW error in SYNC CMD" once,
+ *	e.g. for testing
  */
 enum iwl_trans_status {
 	STATUS_SYNC_HCMD_ACTIVE,
@@ -320,6 +322,7 @@
 	STATUS_TRANS_IDLE,
 	STATUS_TA_ACTIVE,
 	STATUS_TRANS_DEAD,
+	STATUS_SUPPRESS_CMD_ERROR_ONCE,
 };
 
 static inline int
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwl7000/iwlwifi/mvm/debugfs.c
index c664c48..c125f20 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/mvm/debugfs.c
@@ -1136,6 +1136,11 @@
 	if (mvm->fw_restart >= 0)
 		mvm->fw_restart++;
 
+	if (count == 6 && !strcmp(buf, "nolog\n")) {
+		set_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status);
+		set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mvm->trans->status);
+	}
+
 	/* take the return value to make compiler happy - it will fail anyway */
 	ret = iwl_mvm_send_cmd_pdu(mvm,
 				   WIDE_ID(LONG_GROUP, REPLY_ERROR),
@@ -1152,6 +1157,9 @@
 	if (!iwl_mvm_firmware_running(mvm))
 		return -EIO;
 
+	if (count == 6 && !strcmp(buf, "nolog\n"))
+		set_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status);
+
 	iwl_force_nmi(mvm->trans);
 
 	return count;
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwl7000/iwlwifi/mvm/mvm.h
index 0878cd2..d9e30a4 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwl7000/iwlwifi/mvm/mvm.h
@@ -1242,6 +1242,8 @@
  * @IWL_MVM_STATUS_FIRMWARE_RUNNING: firmware is running
  * @IWL_MVM_STATUS_NEED_FLUSH_P2P: need to flush P2P bcast STA
  * @IWL_MVM_STATUS_IN_D3: in D3 (or at least about to go into it)
+ * @IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE: suppress one error log
+ *	if this is set, when intentionally triggered
  */
 enum iwl_mvm_status {
 	IWL_MVM_STATUS_HW_RFKILL,
@@ -1253,6 +1255,7 @@
 	IWL_MVM_STATUS_FIRMWARE_RUNNING,
 	IWL_MVM_STATUS_NEED_FLUSH_P2P,
 	IWL_MVM_STATUS_IN_D3,
+	IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE,
 };
 
 struct iwl_mvm_csme_conn_info {
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwl7000/iwlwifi/mvm/ops.c
index b8e1138..6ac422a 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/mvm/ops.c
@@ -2063,7 +2063,9 @@
 {
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
-	if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status))
+	if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) &&
+	    !test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE,
+				&mvm->status))
 		iwl_mvm_dump_nic_error_log(mvm);
 
 	if (sync) {
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwl7000/iwlwifi/mvm/time-event.c
index 36e61a9..e681331 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/mvm/time-event.c
@@ -662,12 +662,13 @@
 					u32 *uid)
 {
 	u32 id;
-	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+	struct iwl_mvm_vif *mvmvif;
 	enum nl80211_iftype iftype;
 
 	if (!te_data->vif)
 		return false;
 
+	mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
 	iftype = te_data->vif->type;
 
 	/*
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwl7000/iwlwifi/pcie/drv.c
index 63f5ab3..f775af9 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/pcie/drv.c
@@ -378,6 +378,7 @@
 	IWL_DEV_INFO(0x43F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL),
 	IWL_DEV_INFO(0x43F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),
 	IWL_DEV_INFO(0x43F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL),
+	IWL_DEV_INFO(0x43F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650s_name),
 	IWL_DEV_INFO(0xA0F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),
 	IWL_DEV_INFO(0xA0F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),
 	IWL_DEV_INFO(0xA0F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL),
diff --git a/drivers/net/wireless/iwl7000/iwlwifi/queue/tx.c b/drivers/net/wireless/iwl7000/iwlwifi/queue/tx.c
index 2bbb5ce..d08172a 100644
--- a/drivers/net/wireless/iwl7000/iwlwifi/queue/tx.c
+++ b/drivers/net/wireless/iwl7000/iwlwifi/queue/tx.c
@@ -1754,8 +1754,11 @@
 	}
 
 	if (test_bit(STATUS_FW_ERROR, &trans->status)) {
-		IWL_ERR(trans, "FW error in SYNC CMD %s\n", cmd_str);
-		dump_stack();
+		if (!test_and_clear_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE,
+					&trans->status)) {
+			IWL_ERR(trans, "FW error in SYNC CMD %s\n", cmd_str);
+			dump_stack();
+		}
 		ret = -EIO;
 		goto cancel;
 	}
diff --git a/drivers/net/wireless/iwl7000/mac80211/agg-tx.c b/drivers/net/wireless/iwl7000/mac80211/agg-tx.c
index c7e5f1b..1167d4b 100644
--- a/drivers/net/wireless/iwl7000/mac80211/agg-tx.c
+++ b/drivers/net/wireless/iwl7000/mac80211/agg-tx.c
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2010, Intel Corporation
  * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2020 Intel Corporation
+ * Copyright (C) 2018 - 2021 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -213,6 +213,8 @@
 	struct ieee80211_txq *txq = sta->sta.txq[tid];
 	struct txq_info *txqi;
 
+	lockdep_assert_held(&sta->ampdu_mlme.mtx);
+
 	if (!txq)
 		return;
 
@@ -290,7 +292,6 @@
 	ieee80211_assign_tid_tx(sta, tid, NULL);
 
 	ieee80211_agg_splice_finish(sta->sdata, tid);
-	ieee80211_agg_start_txq(sta, tid, false);
 
 	kfree_rcu(tid_tx, rcu_head);
 }
@@ -889,6 +890,7 @@
 {
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	bool send_delba = false;
+	bool start_txq = false;
 
 	ht_dbg(sdata, "Stopping Tx BA session for %pM tid %d\n",
 	       sta->sta.addr, tid);
@@ -906,10 +908,14 @@
 		send_delba = true;
 
 	ieee80211_remove_tid_tx(sta, tid);
+	start_txq = true;
 
  unlock_sta:
 	spin_unlock_bh(&sta->lock);
 
+	if (start_txq)
+		ieee80211_agg_start_txq(sta, tid, false);
+
 	if (send_delba)
 		ieee80211_send_delba(sdata, sta->sta.addr, tid,
 			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
diff --git a/drivers/net/wireless/iwl7000/mac80211/mlme.c b/drivers/net/wireless/iwl7000/mac80211/mlme.c
index a669f17..00b04c9 100644
--- a/drivers/net/wireless/iwl7000/mac80211/mlme.c
+++ b/drivers/net/wireless/iwl7000/mac80211/mlme.c
@@ -5000,13 +5000,20 @@
 	rcu_read_unlock();
 }
 
-static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
-				     struct cfg80211_bss *cbss)
+static u8 ieee80211_max_rx_chains(struct ieee80211_sub_if_data *sdata,
+				  struct cfg80211_bss *cbss)
 {
+	struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	const u8 *ht_cap_ie, *vht_cap_ie;
+	const struct cfg80211_bss_ies *ies;
 	const struct ieee80211_ht_cap *ht_cap;
 	const struct ieee80211_vht_cap *vht_cap;
+	const struct ieee80211_he_cap_elem *he_cap;
+	const struct element *he_cap_elem;
+	u16 mcs_80_map, mcs_160_map;
+	u8 i, mcs_nss_size;
+	bool support_160;
 	u8 chains = 1;
 
 	if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
@@ -5041,6 +5048,53 @@
 		chains = max(chains, nss);
 	}
 
+	if (ifmgd->flags & IEEE80211_STA_DISABLE_HE)
+		return chains;
+
+	ies = rcu_dereference(cbss->ies);
+	he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+					     ies->data, ies->len);
+
+	if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap))
+		return chains;
+
+	he_cap = (void *)(he_cap_elem->data);
+	mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
+
+	/* invalid HE IE */
+	if (he_cap_elem->datalen < mcs_nss_size + sizeof(*he_cap))
+		return chains;
+
+	/* mcs_nss is right after he_cap info */
+	he_mcs_nss_supp = (void *)(he_cap + 1);
+
+	mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
+
+	for (i = 7; i >= 0; i--) {
+		u8 mcs_80 = mcs_80_map >> (2 * i) & 3;
+
+		if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+			chains = max_t(u8, chains, i + 1);
+			break;
+		}
+	}
+
+	support_160 = he_cap->phy_cap_info[0] &
+		      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+	if (!support_160)
+		return chains;
+
+	mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160);
+	for (i = 7; i >= 0; i--) {
+		u8 mcs_160 = mcs_160_map >> (2 * i) & 3;
+
+		if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+			chains = max_t(u8, chains, i + 1);
+			break;
+		}
+	}
+
 	return chains;
 }
 
@@ -5249,7 +5303,7 @@
 						     s1g_oper,
 						     &chandef, false);
 
-	sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
+	sdata->needed_rx_chains = min(ieee80211_max_rx_chains(sdata, cbss),
 				      local->rx_chains);
 
 	rcu_read_unlock();