diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 148 | 
1 files changed, 84 insertions, 64 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 87d6de7efdd2..d87a6bb3e456 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -130,7 +130,7 @@ enum nvm_sku_bits {  /*   * These are the channel numbers in the order that they are stored in the NVM   */ -static const u8 iwl_nvm_channels[] = { +static const u16 iwl_nvm_channels[] = {  	/* 2.4 GHz */  	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,  	/* 5 GHz */ @@ -139,7 +139,7 @@ static const u8 iwl_nvm_channels[] = {  	149, 153, 157, 161, 165  }; -static const u8 iwl_ext_nvm_channels[] = { +static const u16 iwl_ext_nvm_channels[] = {  	/* 2.4 GHz */  	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,  	/* 5 GHz */ @@ -148,14 +148,27 @@ static const u8 iwl_ext_nvm_channels[] = {  	149, 153, 157, 161, 165, 169, 173, 177, 181  }; +static const u16 iwl_uhb_nvm_channels[] = { +	/* 2.4 GHz */ +	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +	/* 5 GHz */ +	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, +	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, +	149, 153, 157, 161, 165, 169, 173, 177, 181, +	/* 6-7 GHz */ +	189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233, 237, 241, +	245, 249, 253, 257, 261, 265, 269, 273, 277, 281, 285, 289, 293, 297, +	301, 305, 309, 313, 317, 321, 325, 329, 333, 337, 341, 345, 349, 353, +	357, 361, 365, 369, 373, 377, 381, 385, 389, 393, 397, 401, 405, 409, +	413, 417, 421 +}; +  #define IWL_NVM_NUM_CHANNELS		ARRAY_SIZE(iwl_nvm_channels)  #define IWL_NVM_NUM_CHANNELS_EXT	ARRAY_SIZE(iwl_ext_nvm_channels) +#define IWL_NVM_NUM_CHANNELS_UHB	ARRAY_SIZE(iwl_uhb_nvm_channels)  #define NUM_2GHZ_CHANNELS		14 -#define NUM_2GHZ_CHANNELS_EXT	14  #define FIRST_2GHZ_HT_MINUS		5  #define LAST_2GHZ_HT_PLUS		9 -#define LAST_5GHZ_HT			165 -#define LAST_5GHZ_HT_FAMILY_8000	181  #define N_HW_ADDR_MASK			0xF  /* rate data (static) */ @@ -213,7 +226,7 @@ enum iwl_nvm_channel_flags {  };  static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, -					       int chan, u16 flags) +					       int chan, u32 flags)  {  #define CHECK_AND_PRINT_I(x)	\  	((flags & NVM_CHANNEL_##x) ? " " #x : "") @@ -244,20 +257,16 @@ static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level,  }  static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, -				 u16 nvm_flags, const struct iwl_cfg *cfg) +				 u32 nvm_flags, const struct iwl_cfg *cfg)  {  	u32 flags = IEEE80211_CHAN_NO_HT40; -	u32 last_5ghz_ht = LAST_5GHZ_HT; - -	if (cfg->nvm_type == IWL_NVM_EXT) -		last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;  	if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) {  		if (ch_num <= LAST_2GHZ_HT_PLUS)  			flags &= ~IEEE80211_CHAN_NO_HT40PLUS;  		if (ch_num >= FIRST_2GHZ_HT_MINUS)  			flags &= ~IEEE80211_CHAN_NO_HT40MINUS; -	} else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) { +	} else if (nvm_flags & NVM_CHANNEL_40MHZ) {  		if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)  			flags &= ~IEEE80211_CHAN_NO_HT40PLUS;  		else @@ -292,30 +301,36 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,  static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,  				struct iwl_nvm_data *data, -				const __le16 * const nvm_ch_flags, -				u32 sbands_flags) +				const void * const nvm_ch_flags, +				u32 sbands_flags, bool v4)  {  	int ch_idx;  	int n_channels = 0;  	struct ieee80211_channel *channel; -	u16 ch_flags; -	int num_of_ch, num_2ghz_channels; -	const u8 *nvm_chan; - -	if (cfg->nvm_type != IWL_NVM_EXT) { -		num_of_ch = IWL_NVM_NUM_CHANNELS; -		nvm_chan = &iwl_nvm_channels[0]; -		num_2ghz_channels = NUM_2GHZ_CHANNELS; -	} else { +	u32 ch_flags; +	int num_of_ch, num_2ghz_channels = NUM_2GHZ_CHANNELS; +	const u16 *nvm_chan; + +	if (cfg->uhb_supported) { +		num_of_ch = IWL_NVM_NUM_CHANNELS_UHB; +		nvm_chan = iwl_uhb_nvm_channels; +	} else if (cfg->nvm_type == IWL_NVM_EXT) {  		num_of_ch = IWL_NVM_NUM_CHANNELS_EXT; -		nvm_chan = &iwl_ext_nvm_channels[0]; -		num_2ghz_channels = NUM_2GHZ_CHANNELS_EXT; +		nvm_chan = iwl_ext_nvm_channels; +	} else { +		num_of_ch = IWL_NVM_NUM_CHANNELS; +		nvm_chan = iwl_nvm_channels;  	}  	for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {  		bool is_5ghz = (ch_idx >= num_2ghz_channels); -		ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); +		if (v4) +			ch_flags = +				__le32_to_cpup((__le32 *)nvm_ch_flags + ch_idx); +		else +			ch_flags = +				__le16_to_cpup((__le16 *)nvm_ch_flags + ch_idx);  		if (is_5ghz && !data->sku_cap_band_52ghz_enable)  			continue; @@ -636,12 +651,7 @@ static struct ieee80211_sband_iftype_data iwl_he_capa[] = {  static void iwl_init_he_hw_capab(struct ieee80211_supported_band *sband,  				 u8 tx_chains, u8 rx_chains)  { -	if (sband->band == NL80211_BAND_2GHZ || -	    sband->band == NL80211_BAND_5GHZ) -		sband->iftype_data = iwl_he_capa; -	else -		return; - +	sband->iftype_data = iwl_he_capa;  	sband->n_iftype_data = ARRAY_SIZE(iwl_he_capa);  	/* If not 2x2, we need to indicate 1x1 in the Midamble RX Max NSTS */ @@ -661,15 +671,15 @@ static void iwl_init_he_hw_capab(struct ieee80211_supported_band *sband,  static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,  			    struct iwl_nvm_data *data, -			    const __le16 *nvm_ch_flags, u8 tx_chains, -			    u8 rx_chains, u32 sbands_flags) +			    const void *nvm_ch_flags, u8 tx_chains, +			    u8 rx_chains, u32 sbands_flags, bool v4)  {  	int n_channels;  	int n_used = 0;  	struct ieee80211_supported_band *sband;  	n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, -					  sbands_flags); +					  sbands_flags, v4);  	sband = &data->bands[NL80211_BAND_2GHZ];  	sband->band = NL80211_BAND_2GHZ;  	sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; @@ -1006,22 +1016,18 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,  		sbands_flags |= IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ;  	iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, -			sbands_flags); +			sbands_flags, false);  	data->calib_version = 255;  	return data;  }  IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); -static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, +static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,  				       int ch_idx, u16 nvm_flags,  				       const struct iwl_cfg *cfg)  {  	u32 flags = NL80211_RRF_NO_HT40; -	u32 last_5ghz_ht = LAST_5GHZ_HT; - -	if (cfg->nvm_type == IWL_NVM_EXT) -		last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;  	if (ch_idx < NUM_2GHZ_CHANNELS &&  	    (nvm_flags & NVM_CHANNEL_40MHZ)) { @@ -1029,8 +1035,7 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,  			flags &= ~NL80211_RRF_NO_HT40PLUS;  		if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)  			flags &= ~NL80211_RRF_NO_HT40MINUS; -	} else if (nvm_chan[ch_idx] <= last_5ghz_ht && -		   (nvm_flags & NVM_CHANNEL_40MHZ)) { +	} else if (nvm_flags & NVM_CHANNEL_40MHZ) {  		if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)  			flags &= ~NL80211_RRF_NO_HT40PLUS;  		else @@ -1074,18 +1079,26 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,  	int ch_idx;  	u16 ch_flags;  	u32 reg_rule_flags, prev_reg_rule_flags = 0; -	const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ? -			     iwl_ext_nvm_channels : iwl_nvm_channels; +	const u16 *nvm_chan;  	struct ieee80211_regdomain *regd, *copy_rd; -	int size_of_regd, regd_to_copy;  	struct ieee80211_reg_rule *rule;  	struct regdb_ptrs *regdb_ptrs;  	enum nl80211_band band;  	int center_freq, prev_center_freq = 0;  	int valid_rules = 0;  	bool new_rule; -	int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ? -			 IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS; +	int max_num_ch; + +	if (cfg->uhb_supported) { +		max_num_ch = IWL_NVM_NUM_CHANNELS_UHB; +		nvm_chan = iwl_uhb_nvm_channels; +	} else if (cfg->nvm_type == IWL_NVM_EXT) { +		max_num_ch = IWL_NVM_NUM_CHANNELS_EXT; +		nvm_chan = iwl_ext_nvm_channels; +	} else { +		max_num_ch = IWL_NVM_NUM_CHANNELS; +		nvm_chan = iwl_nvm_channels; +	}  	if (WARN_ON(num_of_ch > max_num_ch))  		num_of_ch = max_num_ch; @@ -1097,11 +1110,7 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,  		      num_of_ch);  	/* build a regdomain rule for every valid channel */ -	size_of_regd = -		sizeof(struct ieee80211_regdomain) + -		num_of_ch * sizeof(struct ieee80211_reg_rule); - -	regd = kzalloc(size_of_regd, GFP_KERNEL); +	regd = kzalloc(struct_size(regd, reg_rules, num_of_ch), GFP_KERNEL);  	if (!regd)  		return ERR_PTR(-ENOMEM); @@ -1177,14 +1186,10 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,  	 * Narrow down regdom for unused regulatory rules to prevent hole  	 * between reg rules to wmm rules.  	 */ -	regd_to_copy = sizeof(struct ieee80211_regdomain) + -		valid_rules * sizeof(struct ieee80211_reg_rule); - -	copy_rd = kmemdup(regd, regd_to_copy, GFP_KERNEL); -	if (!copy_rd) { +	copy_rd = kmemdup(regd, struct_size(regd, reg_rules, valid_rules), +			  GFP_KERNEL); +	if (!copy_rd)  		copy_rd = ERR_PTR(-ENOMEM); -		goto out; -	}  out:  	kfree(regdb_ptrs); @@ -1393,7 +1398,6 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,  				 const struct iwl_fw *fw)  {  	struct iwl_nvm_get_info cmd = {}; -	struct iwl_nvm_get_info_rsp *rsp;  	struct iwl_nvm_data *nvm;  	struct iwl_host_cmd hcmd = {  		.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, @@ -1408,12 +1412,24 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,  	bool empty_otp;  	u32 mac_flags;  	u32 sbands_flags = 0; +	/* +	 * All the values in iwl_nvm_get_info_rsp v4 are the same as +	 * in v3, except for the channel profile part of the +	 * regulatory.  So we can just access the new struct, with the +	 * exception of the latter. +	 */ +	struct iwl_nvm_get_info_rsp *rsp; +	struct iwl_nvm_get_info_rsp_v3 *rsp_v3; +	bool v4 = fw_has_api(&fw->ucode_capa, +			     IWL_UCODE_TLV_API_REGULATORY_NVM_INFO); +	size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3); +	void *channel_profile;  	ret = iwl_trans_send_cmd(trans, &hcmd);  	if (ret)  		return ERR_PTR(ret); -	if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp), +	if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size,  		 "Invalid payload len in NVM response from FW %d",  		 iwl_rx_packet_payload_len(hcmd.resp_pkt))) {  		ret = -EINVAL; @@ -1475,11 +1491,15 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,  		sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR;  	} +	rsp_v3 = (void *)rsp; +	channel_profile = v4 ? (void *)rsp->regulatory.channel_profile : +			  (void *)rsp_v3->regulatory.channel_profile; +  	iwl_init_sbands(trans->dev, trans->cfg, nvm, -			rsp->regulatory.channel_profile, +			channel_profile,  			nvm->valid_tx_ant & fw->valid_tx_ant,  			nvm->valid_rx_ant & fw->valid_rx_ant, -			sbands_flags); +			sbands_flags, v4);  	iwl_free_resp(&hcmd);  	return nvm;  |