diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2020-12-15 10:48:07 +0100 | 
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2020-12-15 10:48:07 +0100 | 
| commit | 3c41e57a1e168d879e923c5583adeae47eec9f64 (patch) | |
| tree | e6272012c4b766189be2821316a3d23d115f5195 /drivers/net/wireless/intel | |
| parent | d14ce74f1fb376ccbbc0b05ded477ada51253729 (diff) | |
| parent | 2f5fbc4305d07725bfebaedb09e57271315691ef (diff) | |
Merge tag 'irqchip-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core
Pull irqchip updates for 5.11 from Marc Zyngier:
  - Preliminary support for managed interrupts on platform devices
  - Correctly identify allocation of MSIs proxyied by another device
  - Remove the fasteoi IPI flow which has been proved useless
  - Generalise the Ocelot support to new SoCs
  - Improve GICv4.1 vcpu entry, matching the corresponding KVM optimisation
  - Work around spurious interrupts on Qualcomm PDC
  - Random fixes and cleanups
Link: https://lore.kernel.org/r/20201212135626.1479884-1-maz@kernel.org
Diffstat (limited to 'drivers/net/wireless/intel')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/api/sta.h | 10 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h | 8 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 10 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 18 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 103 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c | 20 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 36 | 
8 files changed, 158 insertions, 52 deletions
| diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index d43e0d3f3a12..052413eef059 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -5,10 +5,9 @@   *   * GPL LICENSE SUMMARY   * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH   * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 - 2019 Intel Corporation + * Copyright(c) 2012-2014, 2018 - 2020 Intel Corporation   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of version 2 of the GNU General Public License as @@ -28,10 +27,9 @@   *   * BSD LICENSE   * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH   * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 - 2019 Intel Corporation + * Copyright(c) 2012-2014, 2018 - 2020 Intel Corporation   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -128,7 +126,9 @@ enum iwl_sta_flags {  	STA_FLG_MAX_AGG_SIZE_256K	= (5 << STA_FLG_MAX_AGG_SIZE_SHIFT),  	STA_FLG_MAX_AGG_SIZE_512K	= (6 << STA_FLG_MAX_AGG_SIZE_SHIFT),  	STA_FLG_MAX_AGG_SIZE_1024K	= (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), -	STA_FLG_MAX_AGG_SIZE_MSK	= (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), +	STA_FLG_MAX_AGG_SIZE_2M		= (8 << STA_FLG_MAX_AGG_SIZE_SHIFT), +	STA_FLG_MAX_AGG_SIZE_4M		= (9 << STA_FLG_MAX_AGG_SIZE_SHIFT), +	STA_FLG_MAX_AGG_SIZE_MSK	= (0xf << STA_FLG_MAX_AGG_SIZE_SHIFT),  	STA_FLG_AGG_MPDU_DENS_SHIFT	= 23,  	STA_FLG_AGG_MPDU_DENS_2US	= (4 << STA_FLG_AGG_MPDU_DENS_SHIFT), diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h index a731f28e101a..53b438d709db 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h @@ -8,7 +8,7 @@   * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH   * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 - 2019 Intel Corporation + * Copyright(c) 2018 - 2020 Intel Corporation   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of version 2 of the GNU General Public License as @@ -31,7 +31,7 @@   * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH   * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 - 2019 Intel Corporation + * Copyright(c) 2018 - 2020 Intel Corporation   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -421,12 +421,14 @@ struct iwl_hs20_roc_res {   *	able to run the GO Negotiation. Will not be fragmented and not   *	repetitive. Valid only on the P2P Device MAC. Only the duration will   *	be taken into account. + * @SESSION_PROTECT_CONF_MAX_ID: not used   */  enum iwl_mvm_session_prot_conf_id {  	SESSION_PROTECT_CONF_ASSOC,  	SESSION_PROTECT_CONF_GO_CLIENT_ASSOC,  	SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV,  	SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION, +	SESSION_PROTECT_CONF_MAX_ID,  }; /* SESSION_PROTECTION_CONF_ID_E_VER_1 */  /** @@ -459,7 +461,7 @@ struct iwl_mvm_session_prot_cmd {   * @mac_id: the mac id for which the session protection started / ended   * @status: 1 means success, 0 means failure   * @start: 1 means the session protection started, 0 means it ended - * @conf_id: the configuration id of the session that started / eneded + * @conf_id: see &enum iwl_mvm_session_prot_conf_id   *   * Note that any session protection will always get two notifications: start   * and end even the firmware could not schedule it. diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index cb9e8e189a1a..1d48c7d7fffd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -147,6 +147,16 @@  #define CSR_MAC_SHADOW_REG_CTL2		(CSR_BASE + 0x0AC)  #define CSR_MAC_SHADOW_REG_CTL2_RX_WAKE	0xFFFF +/* LTR control (since IWL_DEVICE_FAMILY_22000) */ +#define CSR_LTR_LONG_VAL_AD			(CSR_BASE + 0x0D4) +#define CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ	0x80000000 +#define CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE	0x1c000000 +#define CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL	0x03ff0000 +#define CSR_LTR_LONG_VAL_AD_SNOOP_REQ		0x00008000 +#define CSR_LTR_LONG_VAL_AD_SNOOP_SCALE		0x00001c00 +#define CSR_LTR_LONG_VAL_AD_SNOOP_VAL		0x000003ff +#define CSR_LTR_LONG_VAL_AD_SCALE_USEC		2 +  /* GIO Chicken Bits (PCI Express bus link power management) */  #define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 688c1125e67b..b627e7da7ac9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3080,7 +3080,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  	/* this would be a mac80211 bug ... but don't crash */  	if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) -		return -EINVAL; +		return test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) ? 0 : -EINVAL;  	/*  	 * If we are in a STA removal flow and in DQA mode: @@ -3127,6 +3127,9 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  			goto out_unlock;  		} +		if (vif->type == NL80211_IFTYPE_STATION) +			vif->bss_conf.he_support = sta->he_cap.has_he; +  		if (sta->tdls &&  		    (vif->p2p ||  		     iwl_mvm_tdls_sta_count(mvm, NULL) == diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 017537944fd0..799d8219463c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -196,6 +196,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		mpdu_dens = sta->ht_cap.ampdu_density;  	} +  	if (sta->vht_cap.vht_supported) {  		agg_size = sta->vht_cap.cap &  			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; @@ -205,6 +206,23 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		agg_size = sta->ht_cap.ampdu_factor;  	} +	/* D6.0 10.12.2 A-MPDU length limit rules +	 * A STA indicates the maximum length of the A-MPDU preEOF padding +	 * that it can receive in an HE PPDU in the Maximum A-MPDU Length +	 * Exponent field in its HT Capabilities, VHT Capabilities, +	 * and HE 6 GHz Band Capabilities elements (if present) and the +	 * Maximum AMPDU Length Exponent Extension field in its HE +	 * Capabilities element +	 */ +	if (sta->he_cap.has_he) +		agg_size += u8_get_bits(sta->he_cap.he_cap_elem.mac_cap_info[3], +					IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); + +	/* Limit to max A-MPDU supported by FW */ +	if (agg_size > (STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT)) +		agg_size = (STA_FLG_MAX_AGG_SIZE_4M >> +			    STA_FLG_MAX_AGG_SIZE_SHIFT); +  	add_sta_cmd.station_flags |=  		cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT);  	add_sta_cmd.station_flags |= diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 7fce79c1c114..1db6d8d38822 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -641,11 +641,32 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,  	}  } +static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, +					      struct iwl_mvm_vif *mvmvif) +{ +	struct iwl_mvm_session_prot_cmd cmd = { +		.id_and_color = +			cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, +							mvmvif->color)), +		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), +		.conf_id = cpu_to_le32(mvmvif->time_event_data.id), +	}; +	int ret; + +	ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, +						   MAC_CONF_GROUP, 0), +				   0, sizeof(cmd), &cmd); +	if (ret) +		IWL_ERR(mvm, +			"Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret); +} +  static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,  					struct iwl_mvm_time_event_data *te_data,  					u32 *uid)  {  	u32 id; +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);  	/*  	 * It is possible that by the time we got to this point the time @@ -663,14 +684,29 @@ static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,  	iwl_mvm_te_clear_data(mvm, te_data);  	spin_unlock_bh(&mvm->time_event_lock); -	/* -	 * It is possible that by the time we try to remove it, the time event -	 * has already ended and removed. In such a case there is no need to -	 * send a removal command. +	/* When session protection is supported, the te_data->id field +	 * is reused to save session protection's configuration.  	 */ -	if (id == TE_MAX) { -		IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); +	if (fw_has_capa(&mvm->fw->ucode_capa, +			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { +		if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) { +			/* Session protection is still ongoing. Cancel it */ +			iwl_mvm_cancel_session_protection(mvm, mvmvif); +			if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { +				set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); +				iwl_mvm_roc_finished(mvm); +			} +		}  		return false; +	} else { +		/* It is possible that by the time we try to remove it, the +		 * time event has already ended and removed. In such a case +		 * there is no need to send a removal command. +		 */ +		if (id == TE_MAX) { +			IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); +			return false; +		}  	}  	return true; @@ -771,6 +807,7 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,  	struct iwl_rx_packet *pkt = rxb_addr(rxb);  	struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data;  	struct ieee80211_vif *vif; +	struct iwl_mvm_vif *mvmvif;  	rcu_read_lock();  	vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id), @@ -779,9 +816,10 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,  	if (!vif)  		goto out_unlock; +	mvmvif = iwl_mvm_vif_from_mac80211(vif); +  	/* The vif is not a P2P_DEVICE, maintain its time_event_data */  	if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { -		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  		struct iwl_mvm_time_event_data *te_data =  			&mvmvif->time_event_data; @@ -816,10 +854,14 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,  	if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) {  		/* End TE, notify mac80211 */ +		mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID;  		ieee80211_remain_on_channel_expired(mvm->hw);  		set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);  		iwl_mvm_roc_finished(mvm);  	} else if (le32_to_cpu(notif->start)) { +		if (WARN_ON(mvmvif->time_event_data.id != +				le32_to_cpu(notif->conf_id))) +			goto out_unlock;  		set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);  		ieee80211_ready_on_channel(mvm->hw); /* Start TE */  	} @@ -845,20 +887,24 @@ iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex); +	/* The time_event_data.id field is reused to save session +	 * protection's configuration. +	 */  	switch (type) {  	case IEEE80211_ROC_TYPE_NORMAL: -		cmd.conf_id = -			cpu_to_le32(SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV); +		mvmvif->time_event_data.id = +			SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV;  		break;  	case IEEE80211_ROC_TYPE_MGMT_TX: -		cmd.conf_id = -			cpu_to_le32(SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION); +		mvmvif->time_event_data.id = +			SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION;  		break;  	default:  		WARN_ONCE(1, "Got an invalid ROC type\n");  		return -EINVAL;  	} +	cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id);  	return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD,  						    MAC_CONF_GROUP, 0),  				    0, sizeof(cmd), &cmd); @@ -960,25 +1006,6 @@ void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)  		__iwl_mvm_remove_time_event(mvm, te_data, &uid);  } -static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, -					      struct iwl_mvm_vif *mvmvif) -{ -	struct iwl_mvm_session_prot_cmd cmd = { -		.id_and_color = -			cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, -							mvmvif->color)), -		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), -	}; -	int ret; - -	ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, -						   MAC_CONF_GROUP, 0), -				   0, sizeof(cmd), &cmd); -	if (ret) -		IWL_ERR(mvm, -			"Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret); -} -  void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif; @@ -988,10 +1015,13 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {  		mvmvif = iwl_mvm_vif_from_mac80211(vif); -		iwl_mvm_cancel_session_protection(mvm, mvmvif); - -		if (vif->type == NL80211_IFTYPE_P2P_DEVICE) +		if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { +			iwl_mvm_cancel_session_protection(mvm, mvmvif);  			set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); +		} else { +			iwl_mvm_remove_aux_roc_te(mvm, mvmvif, +						  &mvmvif->time_event_data); +		}  		iwl_mvm_roc_finished(mvm); @@ -1126,10 +1156,15 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm,  			cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,  							mvmvif->color)),  		.action = cpu_to_le32(FW_CTXT_ACTION_ADD), -		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),  		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),  	}; +	/* The time_event_data.id field is reused to save session +	 * protection's configuration. +	 */ +	mvmvif->time_event_data.id = SESSION_PROTECT_CONF_ASSOC; +	cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id); +  	lockdep_assert_held(&mvm->mutex);  	spin_lock_bh(&mvm->time_event_lock); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index a0352fa873d9..5512e3c630c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -252,6 +252,26 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,  	iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,  		    CSR_AUTO_FUNC_BOOT_ENA); + +	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { +		/* +		 * The firmware initializes this again later (to a smaller +		 * value), but for the boot process initialize the LTR to +		 * ~250 usec. +		 */ +		u32 val = CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ | +			  u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC, +					  CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE) | +			  u32_encode_bits(250, +					  CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL) | +			  CSR_LTR_LONG_VAL_AD_SNOOP_REQ | +			  u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC, +					  CSR_LTR_LONG_VAL_AD_SNOOP_SCALE) | +			  u32_encode_bits(250, CSR_LTR_LONG_VAL_AD_SNOOP_VAL); + +		iwl_write32(trans, CSR_LTR_LONG_VAL_AD, val); +	} +  	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)  		iwl_write_umac_prph(trans, UREG_CPU_INIT_RUN, 1);  	else diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d2e69ad53b27..2fffbbc8462f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2156,18 +2156,36 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,  				   void *buf, int dwords)  {  	unsigned long flags; -	int offs, ret = 0; +	int offs = 0;  	u32 *vals = buf; -	if (iwl_trans_grab_nic_access(trans, &flags)) { -		iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); -		for (offs = 0; offs < dwords; offs++) -			vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); -		iwl_trans_release_nic_access(trans, &flags); -	} else { -		ret = -EBUSY; +	while (offs < dwords) { +		/* limit the time we spin here under lock to 1/2s */ +		ktime_t timeout = ktime_add_us(ktime_get(), 500 * USEC_PER_MSEC); + +		if (iwl_trans_grab_nic_access(trans, &flags)) { +			iwl_write32(trans, HBUS_TARG_MEM_RADDR, +				    addr + 4 * offs); + +			while (offs < dwords) { +				vals[offs] = iwl_read32(trans, +							HBUS_TARG_MEM_RDAT); +				offs++; + +				/* calling ktime_get is expensive so +				 * do it once in 128 reads +				 */ +				if (offs % 128 == 0 && ktime_after(ktime_get(), +								   timeout)) +					break; +			} +			iwl_trans_release_nic_access(trans, &flags); +		} else { +			return -EBUSY; +		}  	} -	return ret; + +	return 0;  }  static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, | 
