diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/fw/pnvm.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 234 | 
1 files changed, 138 insertions, 96 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index c6f2672fdc73..650e4bde9c17 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright(c) 2020-2022 Intel Corporation + * Copyright(c) 2020-2023 Intel Corporation   */  #include "iwl-drv.h" @@ -31,18 +31,18 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,  }  static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, -				   size_t len) +				   size_t len, +				   struct iwl_pnvm_image *pnvm_data)  {  	const struct iwl_ucode_tlv *tlv;  	u32 sha1 = 0;  	u16 mac_type = 0, rf_id = 0; -	u8 *pnvm_data = NULL, *tmp;  	bool hw_match = false; -	u32 size = 0; -	int ret;  	IWL_DEBUG_FW(trans, "Handling PNVM section\n"); +	memset(pnvm_data, 0, sizeof(*pnvm_data)); +  	while (len >= sizeof(*tlv)) {  		u32 tlv_len, tlv_type; @@ -55,8 +55,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,  		if (len < tlv_len) {  			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",  				len, tlv_len); -			ret = -EINVAL; -			goto out; +			return -EINVAL;  		}  		data += sizeof(*tlv); @@ -75,6 +74,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,  			IWL_DEBUG_FW(trans,  				     "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",  				     sha1); +			pnvm_data->version = sha1;  			break;  		case IWL_UCODE_TLV_HW_TYPE:  			if (tlv_len < 2 * sizeof(__le16)) { @@ -112,26 +112,26 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,  				break;  			} -			IWL_DEBUG_FW(trans, "Adding data (size %d)\n", -				     data_len); - -			tmp = krealloc(pnvm_data, size + data_len, GFP_KERNEL); -			if (!tmp) { +			if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) {  				IWL_DEBUG_FW(trans, -					     "Couldn't allocate (more) pnvm_data\n"); - -				ret = -ENOMEM; -				goto out; +					     "too many payloads to allocate in DRAM.\n"); +				return -EINVAL;  			} -			pnvm_data = tmp; - -			memcpy(pnvm_data + size, section->data, data_len); +			IWL_DEBUG_FW(trans, "Adding data (size %d)\n", +				     data_len); -			size += data_len; +			pnvm_data->chunks[pnvm_data->n_chunks].data = section->data; +			pnvm_data->chunks[pnvm_data->n_chunks].len = data_len; +			pnvm_data->n_chunks++;  			break;  		} +		case IWL_UCODE_TLV_MEM_DESC: +			if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len, +							 pnvm_data)) +				return -EINVAL; +			break;  		case IWL_UCODE_TLV_PNVM_SKU:  			IWL_DEBUG_FW(trans,  				     "New PNVM section started, stop parsing.\n"); @@ -152,26 +152,20 @@ done:  			     "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n",  			     CSR_HW_REV_TYPE(trans->hw_rev),  			     CSR_HW_RFID_TYPE(trans->hw_rf_id)); -		ret = -ENOENT; -		goto out; +		return -ENOENT;  	} -	if (!size) { +	if (!pnvm_data->n_chunks) {  		IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n"); -		ret = -ENOENT; -		goto out; +		return -ENOENT;  	} -	IWL_INFO(trans, "loaded PNVM version %08x\n", sha1); - -	ret = iwl_trans_set_pnvm(trans, pnvm_data, size); -out: -	kfree(pnvm_data); -	return ret; +	return 0;  }  static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, -			  size_t len) +			  size_t len, +			  struct iwl_pnvm_image *pnvm_data)  {  	const struct iwl_ucode_tlv *tlv; @@ -212,7 +206,8 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,  			    trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {  				int ret; -				ret = iwl_pnvm_handle_section(trans, data, len); +				ret = iwl_pnvm_handle_section(trans, data, len, +							      pnvm_data);  				if (!ret)  					return 0;  			} else { @@ -255,89 +250,136 @@ static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)  	return 0;  } -int iwl_pnvm_load(struct iwl_trans *trans, -		  struct iwl_notif_wait_data *notif_wait) +static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len)  { -	u8 *data; -	size_t len;  	struct pnvm_sku_package *package; -	struct iwl_notification_wait pnvm_wait; -	static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP, -						PNVM_INIT_COMPLETE_NTFY) }; -	int ret; - -	/* if the SKU_ID is empty, there's nothing to do */ -	if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2]) -		return 0; - -	/* -	 * If we already loaded (or tried to load) it before, we just -	 * need to set it again. -	 */ -	if (trans->pnvm_loaded) { -		ret = iwl_trans_set_pnvm(trans, NULL, 0); -		if (ret) -			return ret; -		goto skip_parse; -	} +	u8 *image = NULL;  	/* First attempt to get the PNVM from BIOS */ -	package = iwl_uefi_get_pnvm(trans, &len); +	package = iwl_uefi_get_pnvm(trans_p, len);  	if (!IS_ERR_OR_NULL(package)) { -		if (len >= sizeof(*package)) { +		if (*len >= sizeof(*package)) {  			/* we need only the data */ -			len -= sizeof(*package); -			data = kmemdup(package->data, len, GFP_KERNEL); -		} else { -			data = NULL; +			*len -= sizeof(*package); +			image = kmemdup(package->data, *len, GFP_KERNEL);  		} -  		/* free package regardless of whether kmemdup succeeded */  		kfree(package); - -		if (data) -			goto parse; +		if (image) +			return image;  	}  	/* If it's not available, try from the filesystem */ -	ret = iwl_pnvm_get_from_fs(trans, &data, &len); +	if (iwl_pnvm_get_from_fs(trans_p, &image, len)) +		return NULL; +	return image; +} + +static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, +					const struct iwl_ucode_capabilities *capa) +{ +	struct iwl_pnvm_image *pnvm_data = NULL; +	u8 *data = NULL; +	size_t length; +	int ret; + +	/* failed to get/parse the image in the past, no use trying again */ +	if (trans->fail_to_parse_pnvm_image) +		return; + +	if (trans->pnvm_loaded) +		goto set; + +	data = iwl_get_pnvm_image(trans, &length); +	if (!data) { +		trans->fail_to_parse_pnvm_image = true; +		return; +	} + +	pnvm_data = kzalloc(sizeof(*pnvm_data), GFP_KERNEL); +	if (!pnvm_data) +		goto free; + +	ret = iwl_pnvm_parse(trans, data, length, pnvm_data);  	if (ret) { -		/* -		 * Pretend we've loaded it - at least we've tried and -		 * couldn't load it at all, so there's no point in -		 * trying again over and over. -		 */ -		trans->pnvm_loaded = true; - -		goto skip_parse; +		trans->fail_to_parse_pnvm_image = true; +		goto free;  	} -parse: -	iwl_pnvm_parse(trans, data, len); +	ret = iwl_trans_load_pnvm(trans, pnvm_data, capa); +	if (ret) +		goto free; +	IWL_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version); +set: +	iwl_trans_set_pnvm(trans, capa); +free:  	kfree(data); +	kfree(pnvm_data); +} -skip_parse: -	/* now try to get the reduce power table, if not loaded yet */ -	if (!trans->reduce_power_loaded) { -		data = iwl_uefi_get_reduced_power(trans, &len); -		if (IS_ERR_OR_NULL(data)) { -			/* -			 * Pretend we've loaded it - at least we've tried and -			 * couldn't load it at all, so there's no point in -			 * trying again over and over. -			 */ -			trans->reduce_power_loaded = true; -		} else { -			ret = iwl_trans_set_reduce_power(trans, data, len); -			if (ret) -				IWL_DEBUG_FW(trans, -					     "Failed to set reduce power table %d\n", -					     ret); -			kfree(data); -		} +static void +iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans, +				    const struct iwl_ucode_capabilities *capa) +{ +	struct iwl_pnvm_image *pnvm_data = NULL; +	u8 *data = NULL; +	size_t length; +	int ret; + +	if (trans->failed_to_load_reduce_power_image) +		return; + +	if (trans->reduce_power_loaded) +		goto set; + +	data = iwl_uefi_get_reduced_power(trans, &length); +	if (IS_ERR(data)) { +		trans->failed_to_load_reduce_power_image = true; +		return;  	} +	pnvm_data = kzalloc(sizeof(*pnvm_data), GFP_KERNEL); +	if (!pnvm_data) +		goto free; + +	ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data); +	if (ret) { +		trans->failed_to_load_reduce_power_image = true; +		goto free; +	} + +	ret = iwl_trans_load_reduce_power(trans, pnvm_data, capa); +	if (ret) { +		IWL_DEBUG_FW(trans, +			     "Failed to load reduce power table %d\n", +			     ret); +		trans->failed_to_load_reduce_power_image = true; +		goto free; +	} + +set: +	iwl_trans_set_reduce_power(trans, capa); +free: +	kfree(data); +	kfree(pnvm_data); +} + +int iwl_pnvm_load(struct iwl_trans *trans, +		  struct iwl_notif_wait_data *notif_wait, +		  const struct iwl_ucode_capabilities *capa) +{ +	struct iwl_notification_wait pnvm_wait; +	static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP, +						PNVM_INIT_COMPLETE_NTFY) }; + +	/* if the SKU_ID is empty, there's nothing to do */ +	if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2]) +		return 0; + +	iwl_pnvm_load_pnvm_to_trans(trans, capa); +	iwl_pnvm_load_reduce_power_to_trans(trans, capa); +  	iwl_init_notification_wait(notif_wait, &pnvm_wait,  				   ntf_cmds, ARRAY_SIZE(ntf_cmds),  				   iwl_pnvm_complete_fn, trans);  |