wifi: iwlwifi: pcie: implement iwl_trans_pcie_gen3_read_mem

Add a gen3-specific implementation of iwl_trans_pcie_read_mem

type=feature
ticket=none

Signed-off-by: Rotem Kerem <rotem.kerem@intel.com>
Change-Id: Ib51603750873bb2ec1fcf69a4630b6b169f8bdaa
Reviewed-on: https://gerritwcs.ir.intel.com/c/iwlwifi-stack-dev/+/197778
automatic-review: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
Tested-by: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
tested: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
x-iwlwifi-stack-dev: 9539694fcab40fcd4ffa1dee8878a0c92febff78
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index f6d6e55..4327163 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -485,7 +485,10 @@
 int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
 		       void *buf, int dwords)
 {
-	return iwl_trans_pcie_read_mem(trans, addr, buf, dwords);
+	if (trans->mac_cfg->gen3)
+		return iwl_trans_pcie_gen3_read_mem(trans, addr, buf, dwords);
+	else
+		return iwl_trans_pcie_read_mem(trans, addr, buf, dwords);
 }
 IWL_EXPORT_SYMBOL(iwl_trans_read_mem);
 
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.c
index f663f34..b58c6e1 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.c
@@ -200,3 +200,55 @@
 	__release(nic_access);
 	spin_unlock(&trans_pcie->reg_lock);
 }
+
+int iwl_trans_pcie_gen3_read_mem(struct iwl_trans *trans, u32 addr,
+				 void *buf, int dwords)
+{
+#define IWL_MAX_HW_ERRS 5
+	unsigned int num_consec_hw_errors = 0;
+	int offs = 0;
+	u32 *vals = buf;
+
+	/* TODO: add support for Vlab (task=vlab) */
+
+	while (offs < dwords) {
+		/* limit the time we spin here under lock to 1/2s */
+		unsigned long end = jiffies + HZ / 2;
+		bool resched = false;
+
+		if (iwl_trans_grab_nic_access(trans)) {
+			iwl_write32(trans, HBUS_TARG_MEM_RADDR,
+				    addr + 4 * offs);
+
+			while (offs < dwords) {
+				vals[offs] = iwl_read32(trans,
+							HBUS_TARG_MEM_RDAT);
+
+				if (iwl_trans_is_hw_error_value(vals[offs]))
+					num_consec_hw_errors++;
+				else
+					num_consec_hw_errors = 0;
+
+				if (num_consec_hw_errors >= IWL_MAX_HW_ERRS) {
+					iwl_trans_release_nic_access(trans);
+					return -EIO;
+				}
+
+				offs++;
+
+				if (time_after(jiffies, end)) {
+					resched = true;
+					break;
+				}
+			}
+			iwl_trans_release_nic_access(trans);
+
+			if (resched)
+				cond_resched();
+		} else {
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.h
index bdf3ef3..ef5705e 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen3/trans.h
@@ -101,5 +101,7 @@
 bool iwl_trans_pcie_gen3_grab_nic_access(struct iwl_trans *trans);
 void __releases(nic_access)
 iwl_trans_pcie_gen3_release_nic_access(struct iwl_trans *trans);
+int iwl_trans_pcie_gen3_read_mem(struct iwl_trans *trans, u32 addr,
+				 void *buf, int dwords);
 
 #endif /* __iwl_trans_pcie_gen3_h__ */
diff --git a/versions b/versions
index b97e24a..9a74b2b 100644
--- a/versions
+++ b/versions
@@ -2,4 +2,4 @@
 BACKPORTED_KERNEL_VERSION="(see git)"
 BACKPORTED_KERNEL_NAME="iwlwifi"
 BACKPORTS_BUILD_TSTAMP=__DATE__ \" \" __TIME__
-BACKPORTS_GIT_TRACKED="iwlwifi-stack-public:master:13855:ceca704e"
+BACKPORTS_GIT_TRACKED="iwlwifi-stack-public:master:13856:9539694f"