diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/nvm.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/nvm.c | 245 | 
1 files changed, 175 insertions, 70 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index edb94ea3165..808f78f6fbf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -5,7 +5,7 @@   *   * GPL LICENSE SUMMARY   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   *   * 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 @@ -30,7 +30,7 @@   *   * BSD LICENSE   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -67,21 +67,19 @@  #include "iwl-eeprom-read.h"  #include "iwl-nvm-parse.h" -/* list of NVM sections we are allowed/need to read */ -static const int nvm_to_read[] = { -	NVM_SECTION_TYPE_HW, -	NVM_SECTION_TYPE_SW, -	NVM_SECTION_TYPE_CALIBRATION, -	NVM_SECTION_TYPE_PRODUCTION, -}; -  /* Default NVM size to read */  #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 6000 +#define IWL_MAX_NVM_SECTION_SIZE 7000  #define NVM_WRITE_OPCODE 1  #define NVM_READ_OPCODE 0 +/* load nvm chunk response */ +enum { +	READ_NVM_CHUNK_SUCCEED = 0, +	READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 +}; +  /*   * prepare the NVM host command w/ the pointers to the nvm buffer   * and send it to fw @@ -98,7 +96,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,  	struct iwl_host_cmd cmd = {  		.id = NVM_ACCESS_CMD,  		.len = { sizeof(struct iwl_nvm_access_cmd), length }, -		.flags = CMD_SYNC | CMD_SEND_IN_RFKILL, +		.flags = CMD_SEND_IN_RFKILL,  		.data = { &nvm_access_cmd, data },  		/* data may come from vmalloc, so use _DUP */  		.dataflags = { 0, IWL_HCMD_DFL_DUP }, @@ -120,7 +118,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,  	struct iwl_rx_packet *pkt;  	struct iwl_host_cmd cmd = {  		.id = NVM_ACCESS_CMD, -		.flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, +		.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,  		.data = { &nvm_access_cmd, },  	};  	int ret, bytes_read, offset_read; @@ -147,10 +145,26 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,  	offset_read = le16_to_cpu(nvm_resp->offset);  	resp_data = nvm_resp->data;  	if (ret) { -		IWL_ERR(mvm, -			"NVM access command failed with status %d (device: %s)\n", -			ret, mvm->cfg->name); -		ret = -EINVAL; +		if ((offset != 0) && +		    (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { +			/* +			 * meaning of NOT_VALID_ADDRESS: +			 * driver try to read chunk from address that is +			 * multiple of 2K and got an error since addr is empty. +			 * meaning of (offset != 0): driver already +			 * read valid data from another chunk so this case +			 * is not an error. +			 */ +			IWL_DEBUG_EEPROM(mvm->trans->dev, +					 "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", +					 offset); +			ret = 0; +		} else { +			IWL_DEBUG_EEPROM(mvm->trans->dev, +					 "NVM access command failed with status %d (device: %s)\n", +					 ret, mvm->cfg->name); +			ret = -EIO; +		}  		goto exit;  	} @@ -219,9 +233,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,  	while (ret == length) {  		ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);  		if (ret < 0) { -			IWL_ERR(mvm, -				"Cannot read NVM from section %d offset %d, length %d\n", -				section, offset, length); +			IWL_DEBUG_EEPROM(mvm->trans->dev, +					 "Cannot read NVM from section %d offset %d, length %d\n", +					 section, offset, length);  			return ret;  		}  		offset += ret; @@ -236,29 +250,53 @@ static struct iwl_nvm_data *  iwl_parse_nvm_sections(struct iwl_mvm *mvm)  {  	struct iwl_nvm_section *sections = mvm->nvm_sections; -	const __le16 *hw, *sw, *calib; +	const __le16 *hw, *sw, *calib, *regulatory, *mac_override;  	/* Checking for required sections */ -	if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || -	    !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) { -		IWL_ERR(mvm, "Can't parse empty NVM sections\n"); -		return NULL; +	if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { +		if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || +		    !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { +			IWL_ERR(mvm, "Can't parse empty NVM sections\n"); +			return NULL; +		} +	} else { +		/* SW and REGULATORY sections are mandatory */ +		if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || +		    !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { +			IWL_ERR(mvm, +				"Can't parse empty family 8000 NVM sections\n"); +			return NULL; +		} +		/* MAC_OVERRIDE or at least HW section must exist */ +		if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && +		    !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { +			IWL_ERR(mvm, +				"Can't parse mac_address, empty sections\n"); +			return NULL; +		}  	}  	if (WARN_ON(!mvm->cfg))  		return NULL; -	hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data; +	hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;  	sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;  	calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; +	regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; +	mac_override = +		(const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; +  	return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, -				  iwl_fw_valid_tx_ant(mvm->fw), -				  iwl_fw_valid_rx_ant(mvm->fw)); +				  regulatory, mac_override, +				  mvm->fw->valid_tx_ant, +				  mvm->fw->valid_rx_ant);  }  #define MAX_NVM_FILE_LEN	16384  /* + * Reads external NVM from a file into mvm->nvm_sections + *   * HOW TO CREATE THE NVM FILE FORMAT:   * ------------------------------   * 1. create hex file, format: @@ -277,19 +315,24 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)   *   * 4. save as "iNVM_xxx.bin" under /lib/firmware   */ -static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) +static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)  { -	int ret, section_id, section_size; +	int ret, section_size; +	u16 section_id;  	const struct firmware *fw_entry;  	const struct {  		__le16 word1;  		__le16 word2;  		u8 data[];  	} *file_sec; -	const u8 *eof; +	const u8 *eof, *temp;  #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))  #define NVM_WORD2_ID(x) (x >> 12) +#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8)) +#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4) + +	IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");  	/*  	 * Obtain NVM image via request_firmware. Since we already used @@ -297,16 +340,16 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)  	 * get here after that we assume the NVM request can be satisfied  	 * synchronously.  	 */ -	ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, +	ret = request_firmware(&fw_entry, mvm->nvm_file_name,  			       mvm->trans->dev);  	if (ret) {  		IWL_ERR(mvm, "ERROR: %s isn't available %d\n", -			iwlwifi_mod_params.nvm_file, ret); +			mvm->nvm_file_name, ret);  		return ret;  	}  	IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", -		 iwlwifi_mod_params.nvm_file, fw_entry->size); +		 mvm->nvm_file_name, fw_entry->size);  	if (fw_entry->size < sizeof(*file_sec)) {  		IWL_ERR(mvm, "NVM file too small\n"); @@ -338,8 +381,16 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)  			break;  		} -		section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); -		section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); +		if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { +			section_size = +				2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); +			section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); +		} else { +			section_size = 2 * NVM_WORD2_LEN_FAMILY_8000( +						le16_to_cpu(file_sec->word2)); +			section_id = NVM_WORD1_ID_FAMILY_8000( +						le16_to_cpu(file_sec->word1)); +		}  		if (section_size > IWL_MAX_NVM_SECTION_SIZE) {  			IWL_ERR(mvm, "ERROR - section too large (%d)\n", @@ -362,12 +413,19 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)  			break;  		} -		ret = iwl_nvm_write_section(mvm, section_id, file_sec->data, -					    section_size); -		if (ret < 0) { -			IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); +		if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, +			 "Invalid NVM section ID %d\n", section_id)) { +			ret = -EINVAL; +			break; +		} + +		temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); +		if (!temp) { +			ret = -ENOMEM;  			break;  		} +		mvm->nvm_sections[section_id].data = temp; +		mvm->nvm_sections[section_id].length = section_size;  		/* advance to the next section */  		file_sec = (void *)(file_sec->data + section_size); @@ -377,45 +435,92 @@ out:  	return ret;  } -int iwl_nvm_init(struct iwl_mvm *mvm) +/* Loads the NVM data stored in mvm->nvm_sections into the NIC */ +int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) +{ +	int i, ret = 0; +	struct iwl_nvm_section *sections = mvm->nvm_sections; + +	IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); + +	for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) { +		if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length) +			continue; +		ret = iwl_nvm_write_section(mvm, i, sections[i].data, +					    sections[i].length); +		if (ret < 0) { +			IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); +			break; +		} +	} +	return ret; +} + +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)  { -	int ret, i, section; +	int ret, section;  	u8 *nvm_buffer, *temp; +	if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) +		return -EINVAL; + +	/* load NVM values from nic */ +	if (read_nvm_from_nic) { +		/* Read From FW NVM */ +		IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + +		nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, +				     GFP_KERNEL); +		if (!nvm_buffer) +			return -ENOMEM; +		for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { +			/* we override the constness for initial read */ +			ret = iwl_nvm_read_section(mvm, section, nvm_buffer); +			if (ret < 0) +				continue; +			temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); +			if (!temp) { +				ret = -ENOMEM; +				break; +			} +			mvm->nvm_sections[section].data = temp; +			mvm->nvm_sections[section].length = ret; + +#ifdef CONFIG_IWLWIFI_DEBUGFS +			switch (section) { +			case NVM_SECTION_TYPE_SW: +				mvm->nvm_sw_blob.data = temp; +				mvm->nvm_sw_blob.size  = ret; +				break; +			case NVM_SECTION_TYPE_CALIBRATION: +				mvm->nvm_calib_blob.data = temp; +				mvm->nvm_calib_blob.size  = ret; +				break; +			case NVM_SECTION_TYPE_PRODUCTION: +				mvm->nvm_prod_blob.data = temp; +				mvm->nvm_prod_blob.size  = ret; +				break; +			default: +				if (section == mvm->cfg->nvm_hw_section_num) { +					mvm->nvm_hw_blob.data = temp; +					mvm->nvm_hw_blob.size = ret; +					break; +				} +			} +#endif +		} +		kfree(nvm_buffer); +	} +  	/* load external NVM if configured */ -	if (iwlwifi_mod_params.nvm_file) { +	if (mvm->nvm_file_name) {  		/* move to External NVM flow */ -		ret = iwl_mvm_load_external_nvm(mvm); +		ret = iwl_mvm_read_external_nvm(mvm);  		if (ret)  			return ret;  	} -	/* Read From FW NVM */ -	IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - -	/* TODO: find correct NVM max size for a section */ -	nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, -			     GFP_KERNEL); -	if (!nvm_buffer) -		return -ENOMEM; -	for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { -		section = nvm_to_read[i]; -		/* we override the constness for initial read */ -		ret = iwl_nvm_read_section(mvm, section, nvm_buffer); -		if (ret < 0) -			break; -		temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); -		if (!temp) { -			ret = -ENOMEM; -			break; -		} -		mvm->nvm_sections[section].data = temp; -		mvm->nvm_sections[section].length = ret; -	} -	kfree(nvm_buffer); -	if (ret < 0) -		return ret; - +	/* parse the relevant nvm sections */  	mvm->nvm_data = iwl_parse_nvm_sections(mvm);  	if (!mvm->nvm_data)  		return -ENODATA;  | 
