diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/nvm.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/nvm.c | 102 |
1 files changed, 56 insertions, 46 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index cf2d09f53782..808f78f6fbf9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c | |||
@@ -74,6 +74,12 @@ | |||
74 | #define NVM_WRITE_OPCODE 1 | 74 | #define NVM_WRITE_OPCODE 1 |
75 | #define NVM_READ_OPCODE 0 | 75 | #define NVM_READ_OPCODE 0 |
76 | 76 | ||
77 | /* load nvm chunk response */ | ||
78 | enum { | ||
79 | READ_NVM_CHUNK_SUCCEED = 0, | ||
80 | READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 | ||
81 | }; | ||
82 | |||
77 | /* | 83 | /* |
78 | * prepare the NVM host command w/ the pointers to the nvm buffer | 84 | * prepare the NVM host command w/ the pointers to the nvm buffer |
79 | * and send it to fw | 85 | * and send it to fw |
@@ -90,7 +96,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, | |||
90 | struct iwl_host_cmd cmd = { | 96 | struct iwl_host_cmd cmd = { |
91 | .id = NVM_ACCESS_CMD, | 97 | .id = NVM_ACCESS_CMD, |
92 | .len = { sizeof(struct iwl_nvm_access_cmd), length }, | 98 | .len = { sizeof(struct iwl_nvm_access_cmd), length }, |
93 | .flags = CMD_SYNC | CMD_SEND_IN_RFKILL, | 99 | .flags = CMD_SEND_IN_RFKILL, |
94 | .data = { &nvm_access_cmd, data }, | 100 | .data = { &nvm_access_cmd, data }, |
95 | /* data may come from vmalloc, so use _DUP */ | 101 | /* data may come from vmalloc, so use _DUP */ |
96 | .dataflags = { 0, IWL_HCMD_DFL_DUP }, | 102 | .dataflags = { 0, IWL_HCMD_DFL_DUP }, |
@@ -112,7 +118,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, | |||
112 | struct iwl_rx_packet *pkt; | 118 | struct iwl_rx_packet *pkt; |
113 | struct iwl_host_cmd cmd = { | 119 | struct iwl_host_cmd cmd = { |
114 | .id = NVM_ACCESS_CMD, | 120 | .id = NVM_ACCESS_CMD, |
115 | .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, | 121 | .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, |
116 | .data = { &nvm_access_cmd, }, | 122 | .data = { &nvm_access_cmd, }, |
117 | }; | 123 | }; |
118 | int ret, bytes_read, offset_read; | 124 | int ret, bytes_read, offset_read; |
@@ -139,10 +145,26 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, | |||
139 | offset_read = le16_to_cpu(nvm_resp->offset); | 145 | offset_read = le16_to_cpu(nvm_resp->offset); |
140 | resp_data = nvm_resp->data; | 146 | resp_data = nvm_resp->data; |
141 | if (ret) { | 147 | if (ret) { |
142 | IWL_ERR(mvm, | 148 | if ((offset != 0) && |
143 | "NVM access command failed with status %d (device: %s)\n", | 149 | (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { |
144 | ret, mvm->cfg->name); | 150 | /* |
145 | ret = -EINVAL; | 151 | * meaning of NOT_VALID_ADDRESS: |
152 | * driver try to read chunk from address that is | ||
153 | * multiple of 2K and got an error since addr is empty. | ||
154 | * meaning of (offset != 0): driver already | ||
155 | * read valid data from another chunk so this case | ||
156 | * is not an error. | ||
157 | */ | ||
158 | IWL_DEBUG_EEPROM(mvm->trans->dev, | ||
159 | "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", | ||
160 | offset); | ||
161 | ret = 0; | ||
162 | } else { | ||
163 | IWL_DEBUG_EEPROM(mvm->trans->dev, | ||
164 | "NVM access command failed with status %d (device: %s)\n", | ||
165 | ret, mvm->cfg->name); | ||
166 | ret = -EIO; | ||
167 | } | ||
146 | goto exit; | 168 | goto exit; |
147 | } | 169 | } |
148 | 170 | ||
@@ -211,9 +233,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, | |||
211 | while (ret == length) { | 233 | while (ret == length) { |
212 | ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); | 234 | ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); |
213 | if (ret < 0) { | 235 | if (ret < 0) { |
214 | IWL_ERR(mvm, | 236 | IWL_DEBUG_EEPROM(mvm->trans->dev, |
215 | "Cannot read NVM from section %d offset %d, length %d\n", | 237 | "Cannot read NVM from section %d offset %d, length %d\n", |
216 | section, offset, length); | 238 | section, offset, length); |
217 | return ret; | 239 | return ret; |
218 | } | 240 | } |
219 | offset += ret; | 241 | offset += ret; |
@@ -238,13 +260,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) | |||
238 | return NULL; | 260 | return NULL; |
239 | } | 261 | } |
240 | } else { | 262 | } else { |
263 | /* SW and REGULATORY sections are mandatory */ | ||
241 | if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || | 264 | if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || |
242 | !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data || | ||
243 | !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { | 265 | !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { |
244 | IWL_ERR(mvm, | 266 | IWL_ERR(mvm, |
245 | "Can't parse empty family 8000 NVM sections\n"); | 267 | "Can't parse empty family 8000 NVM sections\n"); |
246 | return NULL; | 268 | return NULL; |
247 | } | 269 | } |
270 | /* MAC_OVERRIDE or at least HW section must exist */ | ||
271 | if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && | ||
272 | !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { | ||
273 | IWL_ERR(mvm, | ||
274 | "Can't parse mac_address, empty sections\n"); | ||
275 | return NULL; | ||
276 | } | ||
248 | } | 277 | } |
249 | 278 | ||
250 | if (WARN_ON(!mvm->cfg)) | 279 | if (WARN_ON(!mvm->cfg)) |
@@ -311,16 +340,16 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) | |||
311 | * get here after that we assume the NVM request can be satisfied | 340 | * get here after that we assume the NVM request can be satisfied |
312 | * synchronously. | 341 | * synchronously. |
313 | */ | 342 | */ |
314 | ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, | 343 | ret = request_firmware(&fw_entry, mvm->nvm_file_name, |
315 | mvm->trans->dev); | 344 | mvm->trans->dev); |
316 | if (ret) { | 345 | if (ret) { |
317 | IWL_ERR(mvm, "ERROR: %s isn't available %d\n", | 346 | IWL_ERR(mvm, "ERROR: %s isn't available %d\n", |
318 | iwlwifi_mod_params.nvm_file, ret); | 347 | mvm->nvm_file_name, ret); |
319 | return ret; | 348 | return ret; |
320 | } | 349 | } |
321 | 350 | ||
322 | IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", | 351 | IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", |
323 | iwlwifi_mod_params.nvm_file, fw_entry->size); | 352 | mvm->nvm_file_name, fw_entry->size); |
324 | 353 | ||
325 | if (fw_entry->size < sizeof(*file_sec)) { | 354 | if (fw_entry->size < sizeof(*file_sec)) { |
326 | IWL_ERR(mvm, "NVM file too small\n"); | 355 | IWL_ERR(mvm, "NVM file too small\n"); |
@@ -427,53 +456,28 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) | |||
427 | return ret; | 456 | return ret; |
428 | } | 457 | } |
429 | 458 | ||
430 | int iwl_nvm_init(struct iwl_mvm *mvm) | 459 | int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) |
431 | { | 460 | { |
432 | int ret, i, section; | 461 | int ret, section; |
433 | u8 *nvm_buffer, *temp; | 462 | u8 *nvm_buffer, *temp; |
434 | int nvm_to_read[NVM_MAX_NUM_SECTIONS]; | ||
435 | int num_of_sections_to_read; | ||
436 | 463 | ||
437 | if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) | 464 | if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) |
438 | return -EINVAL; | 465 | return -EINVAL; |
439 | 466 | ||
440 | /* load external NVM if configured */ | 467 | /* load NVM values from nic */ |
441 | if (iwlwifi_mod_params.nvm_file) { | 468 | if (read_nvm_from_nic) { |
442 | /* move to External NVM flow */ | ||
443 | ret = iwl_mvm_read_external_nvm(mvm); | ||
444 | if (ret) | ||
445 | return ret; | ||
446 | } else { | ||
447 | /* list of NVM sections we are allowed/need to read */ | ||
448 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { | ||
449 | nvm_to_read[0] = mvm->cfg->nvm_hw_section_num; | ||
450 | nvm_to_read[1] = NVM_SECTION_TYPE_SW; | ||
451 | nvm_to_read[2] = NVM_SECTION_TYPE_CALIBRATION; | ||
452 | nvm_to_read[3] = NVM_SECTION_TYPE_PRODUCTION; | ||
453 | num_of_sections_to_read = 4; | ||
454 | } else { | ||
455 | nvm_to_read[0] = NVM_SECTION_TYPE_SW; | ||
456 | nvm_to_read[1] = NVM_SECTION_TYPE_CALIBRATION; | ||
457 | nvm_to_read[2] = NVM_SECTION_TYPE_PRODUCTION; | ||
458 | nvm_to_read[3] = NVM_SECTION_TYPE_REGULATORY; | ||
459 | nvm_to_read[4] = NVM_SECTION_TYPE_MAC_OVERRIDE; | ||
460 | num_of_sections_to_read = 5; | ||
461 | } | ||
462 | |||
463 | /* Read From FW NVM */ | 469 | /* Read From FW NVM */ |
464 | IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); | 470 | IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); |
465 | 471 | ||
466 | /* TODO: find correct NVM max size for a section */ | ||
467 | nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, | 472 | nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, |
468 | GFP_KERNEL); | 473 | GFP_KERNEL); |
469 | if (!nvm_buffer) | 474 | if (!nvm_buffer) |
470 | return -ENOMEM; | 475 | return -ENOMEM; |
471 | for (i = 0; i < num_of_sections_to_read; i++) { | 476 | for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { |
472 | section = nvm_to_read[i]; | ||
473 | /* we override the constness for initial read */ | 477 | /* we override the constness for initial read */ |
474 | ret = iwl_nvm_read_section(mvm, section, nvm_buffer); | 478 | ret = iwl_nvm_read_section(mvm, section, nvm_buffer); |
475 | if (ret < 0) | 479 | if (ret < 0) |
476 | break; | 480 | continue; |
477 | temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); | 481 | temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); |
478 | if (!temp) { | 482 | if (!temp) { |
479 | ret = -ENOMEM; | 483 | ret = -ENOMEM; |
@@ -502,15 +506,21 @@ int iwl_nvm_init(struct iwl_mvm *mvm) | |||
502 | mvm->nvm_hw_blob.size = ret; | 506 | mvm->nvm_hw_blob.size = ret; |
503 | break; | 507 | break; |
504 | } | 508 | } |
505 | WARN(1, "section: %d", section); | ||
506 | } | 509 | } |
507 | #endif | 510 | #endif |
508 | } | 511 | } |
509 | kfree(nvm_buffer); | 512 | kfree(nvm_buffer); |
510 | if (ret < 0) | 513 | } |
514 | |||
515 | /* load external NVM if configured */ | ||
516 | if (mvm->nvm_file_name) { | ||
517 | /* move to External NVM flow */ | ||
518 | ret = iwl_mvm_read_external_nvm(mvm); | ||
519 | if (ret) | ||
511 | return ret; | 520 | return ret; |
512 | } | 521 | } |
513 | 522 | ||
523 | /* parse the relevant nvm sections */ | ||
514 | mvm->nvm_data = iwl_parse_nvm_sections(mvm); | 524 | mvm->nvm_data = iwl_parse_nvm_sections(mvm); |
515 | if (!mvm->nvm_data) | 525 | if (!mvm->nvm_data) |
516 | return -ENODATA; | 526 | return -ENODATA; |