156a62fc8SJesse Brandeburg /*******************************************************************************
256a62fc8SJesse Brandeburg  *
356a62fc8SJesse Brandeburg  * Intel Ethernet Controller XL710 Family Linux Driver
4dc641b73SGreg Rose  * Copyright(c) 2013 - 2014 Intel Corporation.
556a62fc8SJesse Brandeburg  *
656a62fc8SJesse Brandeburg  * This program is free software; you can redistribute it and/or modify it
756a62fc8SJesse Brandeburg  * under the terms and conditions of the GNU General Public License,
856a62fc8SJesse Brandeburg  * version 2, as published by the Free Software Foundation.
956a62fc8SJesse Brandeburg  *
1056a62fc8SJesse Brandeburg  * This program is distributed in the hope it will be useful, but WITHOUT
1156a62fc8SJesse Brandeburg  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1256a62fc8SJesse Brandeburg  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1356a62fc8SJesse Brandeburg  * more details.
1456a62fc8SJesse Brandeburg  *
15dc641b73SGreg Rose  * You should have received a copy of the GNU General Public License along
16dc641b73SGreg Rose  * with this program.  If not, see <http://www.gnu.org/licenses/>.
1756a62fc8SJesse Brandeburg  *
1856a62fc8SJesse Brandeburg  * The full GNU General Public License is included in this distribution in
1956a62fc8SJesse Brandeburg  * the file called "COPYING".
2056a62fc8SJesse Brandeburg  *
2156a62fc8SJesse Brandeburg  * Contact Information:
2256a62fc8SJesse Brandeburg  * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
2356a62fc8SJesse Brandeburg  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
2456a62fc8SJesse Brandeburg  *
2556a62fc8SJesse Brandeburg  ******************************************************************************/
2656a62fc8SJesse Brandeburg 
2756a62fc8SJesse Brandeburg #include "i40e_prototype.h"
2856a62fc8SJesse Brandeburg 
2956a62fc8SJesse Brandeburg /**
303e26186dSShannon Nelson  * i40e_init_nvm_ops - Initialize NVM function pointers
313e26186dSShannon Nelson  * @hw: pointer to the HW structure
3256a62fc8SJesse Brandeburg  *
333e26186dSShannon Nelson  * Setup the function pointers and the NVM info structure. Should be called
3456a62fc8SJesse Brandeburg  * once per NVM initialization, e.g. inside the i40e_init_shared_code().
3556a62fc8SJesse Brandeburg  * Please notice that the NVM term is used here (& in all methods covered
3656a62fc8SJesse Brandeburg  * in this file) as an equivalent of the FLASH part mapped into the SR.
3756a62fc8SJesse Brandeburg  * We are accessing FLASH always thru the Shadow RAM.
3856a62fc8SJesse Brandeburg  **/
3956a62fc8SJesse Brandeburg i40e_status i40e_init_nvm(struct i40e_hw *hw)
4056a62fc8SJesse Brandeburg {
4156a62fc8SJesse Brandeburg 	struct i40e_nvm_info *nvm = &hw->nvm;
4256a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
4356a62fc8SJesse Brandeburg 	u32 fla, gens;
4456a62fc8SJesse Brandeburg 	u8 sr_size;
4556a62fc8SJesse Brandeburg 
4656a62fc8SJesse Brandeburg 	/* The SR size is stored regardless of the nvm programming mode
4756a62fc8SJesse Brandeburg 	 * as the blank mode may be used in the factory line.
4856a62fc8SJesse Brandeburg 	 */
4956a62fc8SJesse Brandeburg 	gens = rd32(hw, I40E_GLNVM_GENS);
5056a62fc8SJesse Brandeburg 	sr_size = ((gens & I40E_GLNVM_GENS_SR_SIZE_MASK) >>
5156a62fc8SJesse Brandeburg 			   I40E_GLNVM_GENS_SR_SIZE_SHIFT);
523e26186dSShannon Nelson 	/* Switching to words (sr_size contains power of 2KB) */
5356a62fc8SJesse Brandeburg 	nvm->sr_size = (1 << sr_size) * I40E_SR_WORDS_IN_1KB;
5456a62fc8SJesse Brandeburg 
553e26186dSShannon Nelson 	/* Check if we are in the normal or blank NVM programming mode */
5656a62fc8SJesse Brandeburg 	fla = rd32(hw, I40E_GLNVM_FLA);
573e26186dSShannon Nelson 	if (fla & I40E_GLNVM_FLA_LOCKED_MASK) { /* Normal programming mode */
583e26186dSShannon Nelson 		/* Max NVM timeout */
5956a62fc8SJesse Brandeburg 		nvm->timeout = I40E_MAX_NVM_TIMEOUT;
6056a62fc8SJesse Brandeburg 		nvm->blank_nvm_mode = false;
613e26186dSShannon Nelson 	} else { /* Blank programming mode */
6256a62fc8SJesse Brandeburg 		nvm->blank_nvm_mode = true;
6356a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_BLANK_MODE;
6474d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "NVM init error: unsupported blank mode.\n");
6556a62fc8SJesse Brandeburg 	}
6656a62fc8SJesse Brandeburg 
6756a62fc8SJesse Brandeburg 	return ret_code;
6856a62fc8SJesse Brandeburg }
6956a62fc8SJesse Brandeburg 
7056a62fc8SJesse Brandeburg /**
713e26186dSShannon Nelson  * i40e_acquire_nvm - Generic request for acquiring the NVM ownership
723e26186dSShannon Nelson  * @hw: pointer to the HW structure
733e26186dSShannon Nelson  * @access: NVM access type (read or write)
7456a62fc8SJesse Brandeburg  *
7556a62fc8SJesse Brandeburg  * This function will request NVM ownership for reading
7656a62fc8SJesse Brandeburg  * via the proper Admin Command.
7756a62fc8SJesse Brandeburg  **/
7856a62fc8SJesse Brandeburg i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
7956a62fc8SJesse Brandeburg 				       enum i40e_aq_resource_access_type access)
8056a62fc8SJesse Brandeburg {
8156a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
8256a62fc8SJesse Brandeburg 	u64 gtime, timeout;
83c509c1deSShannon Nelson 	u64 time_left = 0;
8456a62fc8SJesse Brandeburg 
8556a62fc8SJesse Brandeburg 	if (hw->nvm.blank_nvm_mode)
8656a62fc8SJesse Brandeburg 		goto i40e_i40e_acquire_nvm_exit;
8756a62fc8SJesse Brandeburg 
8856a62fc8SJesse Brandeburg 	ret_code = i40e_aq_request_resource(hw, I40E_NVM_RESOURCE_ID, access,
89c509c1deSShannon Nelson 					    0, &time_left, NULL);
903e26186dSShannon Nelson 	/* Reading the Global Device Timer */
9156a62fc8SJesse Brandeburg 	gtime = rd32(hw, I40E_GLVFGEN_TIMER);
9256a62fc8SJesse Brandeburg 
933e26186dSShannon Nelson 	/* Store the timeout */
94c509c1deSShannon Nelson 	hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time_left) + gtime;
9556a62fc8SJesse Brandeburg 
96a3f0b381SShannon Nelson 	if (ret_code)
97a3f0b381SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
98a3f0b381SShannon Nelson 			   "NVM acquire type %d failed time_left=%llu ret=%d aq_err=%d\n",
99a3f0b381SShannon Nelson 			   access, time_left, ret_code, hw->aq.asq_last_status);
100a3f0b381SShannon Nelson 
101a3f0b381SShannon Nelson 	if (ret_code && time_left) {
1023e26186dSShannon Nelson 		/* Poll until the current NVM owner timeouts */
103c509c1deSShannon Nelson 		timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT) + gtime;
104a3f0b381SShannon Nelson 		while ((gtime < timeout) && time_left) {
10556a62fc8SJesse Brandeburg 			usleep_range(10000, 20000);
106c509c1deSShannon Nelson 			gtime = rd32(hw, I40E_GLVFGEN_TIMER);
10756a62fc8SJesse Brandeburg 			ret_code = i40e_aq_request_resource(hw,
10856a62fc8SJesse Brandeburg 							I40E_NVM_RESOURCE_ID,
109c509c1deSShannon Nelson 							access, 0, &time_left,
11056a62fc8SJesse Brandeburg 							NULL);
11156a62fc8SJesse Brandeburg 			if (!ret_code) {
11256a62fc8SJesse Brandeburg 				hw->nvm.hw_semaphore_timeout =
113c509c1deSShannon Nelson 					    I40E_MS_TO_GTIME(time_left) + gtime;
11456a62fc8SJesse Brandeburg 				break;
11556a62fc8SJesse Brandeburg 			}
11656a62fc8SJesse Brandeburg 		}
11756a62fc8SJesse Brandeburg 		if (ret_code) {
11856a62fc8SJesse Brandeburg 			hw->nvm.hw_semaphore_timeout = 0;
11974d0d0edSShannon Nelson 			i40e_debug(hw, I40E_DEBUG_NVM,
120a3f0b381SShannon Nelson 				   "NVM acquire timed out, wait %llu ms before trying again. status=%d aq_err=%d\n",
121a3f0b381SShannon Nelson 				   time_left, ret_code, hw->aq.asq_last_status);
12256a62fc8SJesse Brandeburg 		}
12356a62fc8SJesse Brandeburg 	}
12456a62fc8SJesse Brandeburg 
12556a62fc8SJesse Brandeburg i40e_i40e_acquire_nvm_exit:
12656a62fc8SJesse Brandeburg 	return ret_code;
12756a62fc8SJesse Brandeburg }
12856a62fc8SJesse Brandeburg 
12956a62fc8SJesse Brandeburg /**
1303e26186dSShannon Nelson  * i40e_release_nvm - Generic request for releasing the NVM ownership
1313e26186dSShannon Nelson  * @hw: pointer to the HW structure
13256a62fc8SJesse Brandeburg  *
13356a62fc8SJesse Brandeburg  * This function will release NVM resource via the proper Admin Command.
13456a62fc8SJesse Brandeburg  **/
13556a62fc8SJesse Brandeburg void i40e_release_nvm(struct i40e_hw *hw)
13656a62fc8SJesse Brandeburg {
13756a62fc8SJesse Brandeburg 	if (!hw->nvm.blank_nvm_mode)
13856a62fc8SJesse Brandeburg 		i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL);
13956a62fc8SJesse Brandeburg }
14056a62fc8SJesse Brandeburg 
14156a62fc8SJesse Brandeburg /**
1423e26186dSShannon Nelson  * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit
1433e26186dSShannon Nelson  * @hw: pointer to the HW structure
14456a62fc8SJesse Brandeburg  *
14556a62fc8SJesse Brandeburg  * Polls the SRCTL Shadow RAM register done bit.
14656a62fc8SJesse Brandeburg  **/
14756a62fc8SJesse Brandeburg static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw)
14856a62fc8SJesse Brandeburg {
14956a62fc8SJesse Brandeburg 	i40e_status ret_code = I40E_ERR_TIMEOUT;
15056a62fc8SJesse Brandeburg 	u32 srctl, wait_cnt;
15156a62fc8SJesse Brandeburg 
1523e26186dSShannon Nelson 	/* Poll the I40E_GLNVM_SRCTL until the done bit is set */
15356a62fc8SJesse Brandeburg 	for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) {
15456a62fc8SJesse Brandeburg 		srctl = rd32(hw, I40E_GLNVM_SRCTL);
15556a62fc8SJesse Brandeburg 		if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) {
15656a62fc8SJesse Brandeburg 			ret_code = 0;
15756a62fc8SJesse Brandeburg 			break;
15856a62fc8SJesse Brandeburg 		}
15956a62fc8SJesse Brandeburg 		udelay(5);
16056a62fc8SJesse Brandeburg 	}
16156a62fc8SJesse Brandeburg 	if (ret_code == I40E_ERR_TIMEOUT)
16274d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set");
16356a62fc8SJesse Brandeburg 	return ret_code;
16456a62fc8SJesse Brandeburg }
16556a62fc8SJesse Brandeburg 
16656a62fc8SJesse Brandeburg /**
167a4bcfbb7SShannon Nelson  * i40e_read_nvm_word - Reads Shadow RAM
1683e26186dSShannon Nelson  * @hw: pointer to the HW structure
1693e26186dSShannon Nelson  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
1703e26186dSShannon Nelson  * @data: word read from the Shadow RAM
17156a62fc8SJesse Brandeburg  *
1723e26186dSShannon Nelson  * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register.
17356a62fc8SJesse Brandeburg  **/
174a4bcfbb7SShannon Nelson i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
17556a62fc8SJesse Brandeburg 					 u16 *data)
17656a62fc8SJesse Brandeburg {
17756a62fc8SJesse Brandeburg 	i40e_status ret_code = I40E_ERR_TIMEOUT;
17856a62fc8SJesse Brandeburg 	u32 sr_reg;
17956a62fc8SJesse Brandeburg 
18056a62fc8SJesse Brandeburg 	if (offset >= hw->nvm.sr_size) {
18174d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
18274d0d0edSShannon Nelson 			   "NVM read error: offset %d beyond Shadow RAM limit %d\n",
18374d0d0edSShannon Nelson 			   offset, hw->nvm.sr_size);
18456a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_PARAM;
18556a62fc8SJesse Brandeburg 		goto read_nvm_exit;
18656a62fc8SJesse Brandeburg 	}
18756a62fc8SJesse Brandeburg 
1883e26186dSShannon Nelson 	/* Poll the done bit first */
18956a62fc8SJesse Brandeburg 	ret_code = i40e_poll_sr_srctl_done_bit(hw);
19056a62fc8SJesse Brandeburg 	if (!ret_code) {
1913e26186dSShannon Nelson 		/* Write the address and start reading */
19256a62fc8SJesse Brandeburg 		sr_reg = (u32)(offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) |
19356a62fc8SJesse Brandeburg 			 (1 << I40E_GLNVM_SRCTL_START_SHIFT);
19456a62fc8SJesse Brandeburg 		wr32(hw, I40E_GLNVM_SRCTL, sr_reg);
19556a62fc8SJesse Brandeburg 
1963e26186dSShannon Nelson 		/* Poll I40E_GLNVM_SRCTL until the done bit is set */
19756a62fc8SJesse Brandeburg 		ret_code = i40e_poll_sr_srctl_done_bit(hw);
19856a62fc8SJesse Brandeburg 		if (!ret_code) {
19956a62fc8SJesse Brandeburg 			sr_reg = rd32(hw, I40E_GLNVM_SRDATA);
20056a62fc8SJesse Brandeburg 			*data = (u16)((sr_reg &
20156a62fc8SJesse Brandeburg 				       I40E_GLNVM_SRDATA_RDDATA_MASK)
20256a62fc8SJesse Brandeburg 				    >> I40E_GLNVM_SRDATA_RDDATA_SHIFT);
20356a62fc8SJesse Brandeburg 		}
20456a62fc8SJesse Brandeburg 	}
20556a62fc8SJesse Brandeburg 	if (ret_code)
20674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
20774d0d0edSShannon Nelson 			   "NVM read error: Couldn't access Shadow RAM address: 0x%x\n",
20856a62fc8SJesse Brandeburg 			   offset);
20956a62fc8SJesse Brandeburg 
21056a62fc8SJesse Brandeburg read_nvm_exit:
21156a62fc8SJesse Brandeburg 	return ret_code;
21256a62fc8SJesse Brandeburg }
21356a62fc8SJesse Brandeburg 
21456a62fc8SJesse Brandeburg /**
2153e26186dSShannon Nelson  * i40e_read_nvm_buffer - Reads Shadow RAM buffer
2163e26186dSShannon Nelson  * @hw: pointer to the HW structure
21756a62fc8SJesse Brandeburg  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
2183e26186dSShannon Nelson  * @words: (in) number of words to read; (out) number of words actually read
2193e26186dSShannon Nelson  * @data: words read from the Shadow RAM
22056a62fc8SJesse Brandeburg  *
22156a62fc8SJesse Brandeburg  * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
22256a62fc8SJesse Brandeburg  * method. The buffer read is preceded by the NVM ownership take
22356a62fc8SJesse Brandeburg  * and followed by the release.
22456a62fc8SJesse Brandeburg  **/
22556a62fc8SJesse Brandeburg i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
22656a62fc8SJesse Brandeburg 					   u16 *words, u16 *data)
22756a62fc8SJesse Brandeburg {
22856a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
22956a62fc8SJesse Brandeburg 	u16 index, word;
23056a62fc8SJesse Brandeburg 
2313e26186dSShannon Nelson 	/* Loop thru the selected region */
23256a62fc8SJesse Brandeburg 	for (word = 0; word < *words; word++) {
23356a62fc8SJesse Brandeburg 		index = offset + word;
234a4bcfbb7SShannon Nelson 		ret_code = i40e_read_nvm_word(hw, index, &data[word]);
23556a62fc8SJesse Brandeburg 		if (ret_code)
23656a62fc8SJesse Brandeburg 			break;
23756a62fc8SJesse Brandeburg 	}
238a4bcfbb7SShannon Nelson 
2393e26186dSShannon Nelson 	/* Update the number of words read from the Shadow RAM */
24056a62fc8SJesse Brandeburg 	*words = word;
24156a62fc8SJesse Brandeburg 
24256a62fc8SJesse Brandeburg 	return ret_code;
24356a62fc8SJesse Brandeburg }
24456a62fc8SJesse Brandeburg 
24556a62fc8SJesse Brandeburg /**
246cd552cb4SShannon Nelson  * i40e_write_nvm_aq - Writes Shadow RAM.
247cd552cb4SShannon Nelson  * @hw: pointer to the HW structure.
248cd552cb4SShannon Nelson  * @module_pointer: module pointer location in words from the NVM beginning
249cd552cb4SShannon Nelson  * @offset: offset in words from module start
250cd552cb4SShannon Nelson  * @words: number of words to write
251cd552cb4SShannon Nelson  * @data: buffer with words to write to the Shadow RAM
252cd552cb4SShannon Nelson  * @last_command: tells the AdminQ that this is the last command
253cd552cb4SShannon Nelson  *
254cd552cb4SShannon Nelson  * Writes a 16 bit words buffer to the Shadow RAM using the admin command.
255cd552cb4SShannon Nelson  **/
256952d9639SWei Yongjun static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
257cd552cb4SShannon Nelson 				     u32 offset, u16 words, void *data,
258cd552cb4SShannon Nelson 				     bool last_command)
259cd552cb4SShannon Nelson {
260cd552cb4SShannon Nelson 	i40e_status ret_code = I40E_ERR_NVM;
261cd552cb4SShannon Nelson 
262cd552cb4SShannon Nelson 	/* Here we are checking the SR limit only for the flat memory model.
263cd552cb4SShannon Nelson 	 * We cannot do it for the module-based model, as we did not acquire
264cd552cb4SShannon Nelson 	 * the NVM resource yet (we cannot get the module pointer value).
265cd552cb4SShannon Nelson 	 * Firmware will check the module-based model.
266cd552cb4SShannon Nelson 	 */
267cd552cb4SShannon Nelson 	if ((offset + words) > hw->nvm.sr_size)
26874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
26974d0d0edSShannon Nelson 			   "NVM write error: offset %d beyond Shadow RAM limit %d\n",
27074d0d0edSShannon Nelson 			   (offset + words), hw->nvm.sr_size);
271cd552cb4SShannon Nelson 	else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS)
272cd552cb4SShannon Nelson 		/* We can write only up to 4KB (one sector), in one AQ write */
27374d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
27474d0d0edSShannon Nelson 			   "NVM write fail error: tried to write %d words, limit is %d.\n",
27574d0d0edSShannon Nelson 			   words, I40E_SR_SECTOR_SIZE_IN_WORDS);
276cd552cb4SShannon Nelson 	else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS)
277cd552cb4SShannon Nelson 		 != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS))
278cd552cb4SShannon Nelson 		/* A single write cannot spread over two sectors */
27974d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
28074d0d0edSShannon Nelson 			   "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n",
28174d0d0edSShannon Nelson 			   offset, words);
282cd552cb4SShannon Nelson 	else
283cd552cb4SShannon Nelson 		ret_code = i40e_aq_update_nvm(hw, module_pointer,
284cd552cb4SShannon Nelson 					      2 * offset,  /*bytes*/
285cd552cb4SShannon Nelson 					      2 * words,   /*bytes*/
286cd552cb4SShannon Nelson 					      data, last_command, NULL);
287cd552cb4SShannon Nelson 
288cd552cb4SShannon Nelson 	return ret_code;
289cd552cb4SShannon Nelson }
290cd552cb4SShannon Nelson 
291cd552cb4SShannon Nelson /**
29256a62fc8SJesse Brandeburg  * i40e_calc_nvm_checksum - Calculates and returns the checksum
29356a62fc8SJesse Brandeburg  * @hw: pointer to hardware structure
29498d44381SJeff Kirsher  * @checksum: pointer to the checksum
29556a62fc8SJesse Brandeburg  *
2963e26186dSShannon Nelson  * This function calculates SW Checksum that covers the whole 64kB shadow RAM
29756a62fc8SJesse Brandeburg  * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD
29856a62fc8SJesse Brandeburg  * is customer specific and unknown. Therefore, this function skips all maximum
29956a62fc8SJesse Brandeburg  * possible size of VPD (1kB).
30056a62fc8SJesse Brandeburg  **/
30156a62fc8SJesse Brandeburg static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
30256a62fc8SJesse Brandeburg 						    u16 *checksum)
30356a62fc8SJesse Brandeburg {
30456a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
30556a62fc8SJesse Brandeburg 	u16 pcie_alt_module = 0;
30656a62fc8SJesse Brandeburg 	u16 checksum_local = 0;
30756a62fc8SJesse Brandeburg 	u16 vpd_module = 0;
30856a62fc8SJesse Brandeburg 	u16 word = 0;
30956a62fc8SJesse Brandeburg 	u32 i = 0;
31056a62fc8SJesse Brandeburg 
31156a62fc8SJesse Brandeburg 	/* read pointer to VPD area */
312a4bcfbb7SShannon Nelson 	ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module);
31356a62fc8SJesse Brandeburg 	if (ret_code) {
31456a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
31556a62fc8SJesse Brandeburg 		goto i40e_calc_nvm_checksum_exit;
31656a62fc8SJesse Brandeburg 	}
31756a62fc8SJesse Brandeburg 
31856a62fc8SJesse Brandeburg 	/* read pointer to PCIe Alt Auto-load module */
319a4bcfbb7SShannon Nelson 	ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR,
32056a62fc8SJesse Brandeburg 				       &pcie_alt_module);
32156a62fc8SJesse Brandeburg 	if (ret_code) {
32256a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
32356a62fc8SJesse Brandeburg 		goto i40e_calc_nvm_checksum_exit;
32456a62fc8SJesse Brandeburg 	}
32556a62fc8SJesse Brandeburg 
32656a62fc8SJesse Brandeburg 	/* Calculate SW checksum that covers the whole 64kB shadow RAM
32756a62fc8SJesse Brandeburg 	 * except the VPD and PCIe ALT Auto-load modules
32856a62fc8SJesse Brandeburg 	 */
32956a62fc8SJesse Brandeburg 	for (i = 0; i < hw->nvm.sr_size; i++) {
33056a62fc8SJesse Brandeburg 		/* Skip Checksum word */
33156a62fc8SJesse Brandeburg 		if (i == I40E_SR_SW_CHECKSUM_WORD)
33256a62fc8SJesse Brandeburg 			i++;
33356a62fc8SJesse Brandeburg 		/* Skip VPD module (convert byte size to word count) */
33456a62fc8SJesse Brandeburg 		if (i == (u32)vpd_module) {
33556a62fc8SJesse Brandeburg 			i += (I40E_SR_VPD_MODULE_MAX_SIZE / 2);
33656a62fc8SJesse Brandeburg 			if (i >= hw->nvm.sr_size)
33756a62fc8SJesse Brandeburg 				break;
33856a62fc8SJesse Brandeburg 		}
33956a62fc8SJesse Brandeburg 		/* Skip PCIe ALT module (convert byte size to word count) */
34056a62fc8SJesse Brandeburg 		if (i == (u32)pcie_alt_module) {
34156a62fc8SJesse Brandeburg 			i += (I40E_SR_PCIE_ALT_MODULE_MAX_SIZE / 2);
34256a62fc8SJesse Brandeburg 			if (i >= hw->nvm.sr_size)
34356a62fc8SJesse Brandeburg 				break;
34456a62fc8SJesse Brandeburg 		}
34556a62fc8SJesse Brandeburg 
346a4bcfbb7SShannon Nelson 		ret_code = i40e_read_nvm_word(hw, (u16)i, &word);
34756a62fc8SJesse Brandeburg 		if (ret_code) {
34856a62fc8SJesse Brandeburg 			ret_code = I40E_ERR_NVM_CHECKSUM;
34956a62fc8SJesse Brandeburg 			goto i40e_calc_nvm_checksum_exit;
35056a62fc8SJesse Brandeburg 		}
35156a62fc8SJesse Brandeburg 		checksum_local += word;
35256a62fc8SJesse Brandeburg 	}
35356a62fc8SJesse Brandeburg 
35456a62fc8SJesse Brandeburg 	*checksum = (u16)I40E_SR_SW_CHECKSUM_BASE - checksum_local;
35556a62fc8SJesse Brandeburg 
35656a62fc8SJesse Brandeburg i40e_calc_nvm_checksum_exit:
35756a62fc8SJesse Brandeburg 	return ret_code;
35856a62fc8SJesse Brandeburg }
35956a62fc8SJesse Brandeburg 
36056a62fc8SJesse Brandeburg /**
361cd552cb4SShannon Nelson  * i40e_update_nvm_checksum - Updates the NVM checksum
362cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
363cd552cb4SShannon Nelson  *
364cd552cb4SShannon Nelson  * NVM ownership must be acquired before calling this function and released
365cd552cb4SShannon Nelson  * on ARQ completion event reception by caller.
366cd552cb4SShannon Nelson  * This function will commit SR to NVM.
367cd552cb4SShannon Nelson  **/
368cd552cb4SShannon Nelson i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw)
369cd552cb4SShannon Nelson {
370cd552cb4SShannon Nelson 	i40e_status ret_code = 0;
371cd552cb4SShannon Nelson 	u16 checksum;
372cd552cb4SShannon Nelson 
373cd552cb4SShannon Nelson 	ret_code = i40e_calc_nvm_checksum(hw, &checksum);
374cd552cb4SShannon Nelson 	if (!ret_code)
375cd552cb4SShannon Nelson 		ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD,
376cd552cb4SShannon Nelson 					     1, &checksum, true);
377cd552cb4SShannon Nelson 
378cd552cb4SShannon Nelson 	return ret_code;
379cd552cb4SShannon Nelson }
380cd552cb4SShannon Nelson 
381cd552cb4SShannon Nelson /**
38256a62fc8SJesse Brandeburg  * i40e_validate_nvm_checksum - Validate EEPROM checksum
38356a62fc8SJesse Brandeburg  * @hw: pointer to hardware structure
38456a62fc8SJesse Brandeburg  * @checksum: calculated checksum
38556a62fc8SJesse Brandeburg  *
38656a62fc8SJesse Brandeburg  * Performs checksum calculation and validates the NVM SW checksum. If the
38756a62fc8SJesse Brandeburg  * caller does not need checksum, the value can be NULL.
38856a62fc8SJesse Brandeburg  **/
38956a62fc8SJesse Brandeburg i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
39056a62fc8SJesse Brandeburg 						 u16 *checksum)
39156a62fc8SJesse Brandeburg {
39256a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
39356a62fc8SJesse Brandeburg 	u16 checksum_sr = 0;
394e15c9fa0SJesse Brandeburg 	u16 checksum_local = 0;
39556a62fc8SJesse Brandeburg 
39656a62fc8SJesse Brandeburg 	ret_code = i40e_calc_nvm_checksum(hw, &checksum_local);
39756a62fc8SJesse Brandeburg 	if (ret_code)
3987a208e83SKamil Krawczyk 		goto i40e_validate_nvm_checksum_exit;
39956a62fc8SJesse Brandeburg 
40056a62fc8SJesse Brandeburg 	/* Do not use i40e_read_nvm_word() because we do not want to take
40156a62fc8SJesse Brandeburg 	 * the synchronization semaphores twice here.
40256a62fc8SJesse Brandeburg 	 */
403a4bcfbb7SShannon Nelson 	i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr);
40456a62fc8SJesse Brandeburg 
40556a62fc8SJesse Brandeburg 	/* Verify read checksum from EEPROM is the same as
40656a62fc8SJesse Brandeburg 	 * calculated checksum
40756a62fc8SJesse Brandeburg 	 */
40856a62fc8SJesse Brandeburg 	if (checksum_local != checksum_sr)
40956a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
41056a62fc8SJesse Brandeburg 
41156a62fc8SJesse Brandeburg 	/* If the user cares, return the calculated checksum */
41256a62fc8SJesse Brandeburg 	if (checksum)
41356a62fc8SJesse Brandeburg 		*checksum = checksum_local;
41456a62fc8SJesse Brandeburg 
41556a62fc8SJesse Brandeburg i40e_validate_nvm_checksum_exit:
41656a62fc8SJesse Brandeburg 	return ret_code;
41756a62fc8SJesse Brandeburg }
418cd552cb4SShannon Nelson 
419cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
420cd552cb4SShannon Nelson 					  struct i40e_nvm_access *cmd,
421cd552cb4SShannon Nelson 					  u8 *bytes, int *errno);
422cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
423cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
424cd552cb4SShannon Nelson 					     u8 *bytes, int *errno);
425cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
426cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
427cd552cb4SShannon Nelson 					     u8 *bytes, int *errno);
428cd552cb4SShannon Nelson static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
429cd552cb4SShannon Nelson 						struct i40e_nvm_access *cmd,
430cd552cb4SShannon Nelson 						int *errno);
431cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
432cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
433cd552cb4SShannon Nelson 					 int *errno);
434cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
435cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
436cd552cb4SShannon Nelson 					 u8 *bytes, int *errno);
437cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
438cd552cb4SShannon Nelson 					struct i40e_nvm_access *cmd,
439cd552cb4SShannon Nelson 					u8 *bytes, int *errno);
440cd552cb4SShannon Nelson static inline u8 i40e_nvmupd_get_module(u32 val)
441cd552cb4SShannon Nelson {
442cd552cb4SShannon Nelson 	return (u8)(val & I40E_NVM_MOD_PNT_MASK);
443cd552cb4SShannon Nelson }
444cd552cb4SShannon Nelson static inline u8 i40e_nvmupd_get_transaction(u32 val)
445cd552cb4SShannon Nelson {
446cd552cb4SShannon Nelson 	return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT);
447cd552cb4SShannon Nelson }
448cd552cb4SShannon Nelson 
44974d0d0edSShannon Nelson static char *i40e_nvm_update_state_str[] = {
45074d0d0edSShannon Nelson 	"I40E_NVMUPD_INVALID",
45174d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_CON",
45274d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_SNT",
45374d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_LCB",
45474d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_SA",
45574d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_ERA",
45674d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_CON",
45774d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_SNT",
45874d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_LCB",
45974d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_SA",
46074d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_CON",
46174d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_SA",
46274d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_LCB",
46374d0d0edSShannon Nelson };
46474d0d0edSShannon Nelson 
465cd552cb4SShannon Nelson /**
466cd552cb4SShannon Nelson  * i40e_nvmupd_command - Process an NVM update command
467cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
468cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command
469cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
470cd552cb4SShannon Nelson  * @errno: pointer to return error code
471cd552cb4SShannon Nelson  *
472cd552cb4SShannon Nelson  * Dispatches command depending on what update state is current
473cd552cb4SShannon Nelson  **/
474cd552cb4SShannon Nelson i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
475cd552cb4SShannon Nelson 				struct i40e_nvm_access *cmd,
476cd552cb4SShannon Nelson 				u8 *bytes, int *errno)
477cd552cb4SShannon Nelson {
478cd552cb4SShannon Nelson 	i40e_status status;
479cd552cb4SShannon Nelson 
480cd552cb4SShannon Nelson 	/* assume success */
481cd552cb4SShannon Nelson 	*errno = 0;
482cd552cb4SShannon Nelson 
483cd552cb4SShannon Nelson 	switch (hw->nvmupd_state) {
484cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_INIT:
485cd552cb4SShannon Nelson 		status = i40e_nvmupd_state_init(hw, cmd, bytes, errno);
486cd552cb4SShannon Nelson 		break;
487cd552cb4SShannon Nelson 
488cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_READING:
489cd552cb4SShannon Nelson 		status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno);
490cd552cb4SShannon Nelson 		break;
491cd552cb4SShannon Nelson 
492cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_WRITING:
493cd552cb4SShannon Nelson 		status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno);
494cd552cb4SShannon Nelson 		break;
495cd552cb4SShannon Nelson 
496cd552cb4SShannon Nelson 	default:
497cd552cb4SShannon Nelson 		/* invalid state, should never happen */
49874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
49974d0d0edSShannon Nelson 			   "NVMUPD: no such state %d\n", hw->nvmupd_state);
500cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
501cd552cb4SShannon Nelson 		*errno = -ESRCH;
502cd552cb4SShannon Nelson 		break;
503cd552cb4SShannon Nelson 	}
504cd552cb4SShannon Nelson 	return status;
505cd552cb4SShannon Nelson }
506cd552cb4SShannon Nelson 
507cd552cb4SShannon Nelson /**
508cd552cb4SShannon Nelson  * i40e_nvmupd_state_init - Handle NVM update state Init
509cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
510cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
511cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
512cd552cb4SShannon Nelson  * @errno: pointer to return error code
513cd552cb4SShannon Nelson  *
514cd552cb4SShannon Nelson  * Process legitimate commands of the Init state and conditionally set next
515cd552cb4SShannon Nelson  * state. Reject all other commands.
516cd552cb4SShannon Nelson  **/
517cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
518cd552cb4SShannon Nelson 					  struct i40e_nvm_access *cmd,
519cd552cb4SShannon Nelson 					  u8 *bytes, int *errno)
520cd552cb4SShannon Nelson {
521cd552cb4SShannon Nelson 	i40e_status status = 0;
522cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
523cd552cb4SShannon Nelson 
524cd552cb4SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
525cd552cb4SShannon Nelson 
526cd552cb4SShannon Nelson 	switch (upd_cmd) {
527cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SA:
528cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
529cd552cb4SShannon Nelson 		if (status) {
530cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
531cd552cb4SShannon Nelson 		} else {
532cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
533cd552cb4SShannon Nelson 			i40e_release_nvm(hw);
534cd552cb4SShannon Nelson 		}
535cd552cb4SShannon Nelson 		break;
536cd552cb4SShannon Nelson 
537cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SNT:
538cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
539cd552cb4SShannon Nelson 		if (status) {
540cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
541cd552cb4SShannon Nelson 		} else {
542cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
5430fdd052cSShannon Nelson 			if (status)
5440fdd052cSShannon Nelson 				i40e_release_nvm(hw);
5450fdd052cSShannon Nelson 			else
546cd552cb4SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_READING;
547cd552cb4SShannon Nelson 		}
548cd552cb4SShannon Nelson 		break;
549cd552cb4SShannon Nelson 
550cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_ERA:
551cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
552cd552cb4SShannon Nelson 		if (status) {
553cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
554cd552cb4SShannon Nelson 		} else {
555cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_erase(hw, cmd, errno);
556cd552cb4SShannon Nelson 			if (status)
557cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
558cd552cb4SShannon Nelson 			else
559cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
560cd552cb4SShannon Nelson 		}
561cd552cb4SShannon Nelson 		break;
562cd552cb4SShannon Nelson 
563cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_SA:
564cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
565cd552cb4SShannon Nelson 		if (status) {
566cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
567cd552cb4SShannon Nelson 		} else {
568cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
569cd552cb4SShannon Nelson 			if (status)
570cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
571cd552cb4SShannon Nelson 			else
572cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
573cd552cb4SShannon Nelson 		}
574cd552cb4SShannon Nelson 		break;
575cd552cb4SShannon Nelson 
576cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_SNT:
577cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
578cd552cb4SShannon Nelson 		if (status) {
579cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
580cd552cb4SShannon Nelson 		} else {
581cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
5820fdd052cSShannon Nelson 			if (status)
5830fdd052cSShannon Nelson 				i40e_release_nvm(hw);
5840fdd052cSShannon Nelson 			else
585cd552cb4SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING;
586cd552cb4SShannon Nelson 		}
587cd552cb4SShannon Nelson 		break;
588cd552cb4SShannon Nelson 
589cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_SA:
590cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
591cd552cb4SShannon Nelson 		if (status) {
592cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
593cd552cb4SShannon Nelson 		} else {
594cd552cb4SShannon Nelson 			status = i40e_update_nvm_checksum(hw);
595cd552cb4SShannon Nelson 			if (status) {
596cd552cb4SShannon Nelson 				*errno = hw->aq.asq_last_status ?
597cd552cb4SShannon Nelson 				   i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
598cd552cb4SShannon Nelson 				   -EIO;
599cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
600cd552cb4SShannon Nelson 			} else {
601cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
602cd552cb4SShannon Nelson 			}
603cd552cb4SShannon Nelson 		}
604cd552cb4SShannon Nelson 		break;
605cd552cb4SShannon Nelson 
606cd552cb4SShannon Nelson 	default:
60774d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
60874d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in init state\n",
60974d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
610cd552cb4SShannon Nelson 		status = I40E_ERR_NVM;
611cd552cb4SShannon Nelson 		*errno = -ESRCH;
612cd552cb4SShannon Nelson 		break;
613cd552cb4SShannon Nelson 	}
614cd552cb4SShannon Nelson 	return status;
615cd552cb4SShannon Nelson }
616cd552cb4SShannon Nelson 
617cd552cb4SShannon Nelson /**
618cd552cb4SShannon Nelson  * i40e_nvmupd_state_reading - Handle NVM update state Reading
619cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
620cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
621cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
622cd552cb4SShannon Nelson  * @errno: pointer to return error code
623cd552cb4SShannon Nelson  *
624cd552cb4SShannon Nelson  * NVM ownership is already held.  Process legitimate commands and set any
625cd552cb4SShannon Nelson  * change in state; reject all other commands.
626cd552cb4SShannon Nelson  **/
627cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
628cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
629cd552cb4SShannon Nelson 					     u8 *bytes, int *errno)
630cd552cb4SShannon Nelson {
631cd552cb4SShannon Nelson 	i40e_status status;
632cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
633cd552cb4SShannon Nelson 
634cd552cb4SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
635cd552cb4SShannon Nelson 
636cd552cb4SShannon Nelson 	switch (upd_cmd) {
637cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SA:
638cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_CON:
639cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
640cd552cb4SShannon Nelson 		break;
641cd552cb4SShannon Nelson 
642cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_LCB:
643cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
644cd552cb4SShannon Nelson 		i40e_release_nvm(hw);
645cd552cb4SShannon Nelson 		hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
646cd552cb4SShannon Nelson 		break;
647cd552cb4SShannon Nelson 
648cd552cb4SShannon Nelson 	default:
64974d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
65074d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in reading state.\n",
65174d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
652cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
653cd552cb4SShannon Nelson 		*errno = -ESRCH;
654cd552cb4SShannon Nelson 		break;
655cd552cb4SShannon Nelson 	}
656cd552cb4SShannon Nelson 	return status;
657cd552cb4SShannon Nelson }
658cd552cb4SShannon Nelson 
659cd552cb4SShannon Nelson /**
660cd552cb4SShannon Nelson  * i40e_nvmupd_state_writing - Handle NVM update state Writing
661cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
662cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
663cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
664cd552cb4SShannon Nelson  * @errno: pointer to return error code
665cd552cb4SShannon Nelson  *
666cd552cb4SShannon Nelson  * NVM ownership is already held.  Process legitimate commands and set any
667cd552cb4SShannon Nelson  * change in state; reject all other commands
668cd552cb4SShannon Nelson  **/
669cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
670cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
671cd552cb4SShannon Nelson 					     u8 *bytes, int *errno)
672cd552cb4SShannon Nelson {
673cd552cb4SShannon Nelson 	i40e_status status;
674cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
675cd552cb4SShannon Nelson 
676cd552cb4SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
677cd552cb4SShannon Nelson 
678cd552cb4SShannon Nelson 	switch (upd_cmd) {
679cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_CON:
680cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
681cd552cb4SShannon Nelson 		break;
682cd552cb4SShannon Nelson 
683cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_LCB:
684cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
6850fdd052cSShannon Nelson 		if (!status)
686cd552cb4SShannon Nelson 			hw->aq.nvm_release_on_done = true;
687cd552cb4SShannon Nelson 		hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
688cd552cb4SShannon Nelson 		break;
689cd552cb4SShannon Nelson 
690cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_CON:
691cd552cb4SShannon Nelson 		status = i40e_update_nvm_checksum(hw);
692cd552cb4SShannon Nelson 		if (status) {
693cd552cb4SShannon Nelson 			*errno = hw->aq.asq_last_status ?
694cd552cb4SShannon Nelson 				   i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
695cd552cb4SShannon Nelson 				   -EIO;
696cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
697cd552cb4SShannon Nelson 		}
698cd552cb4SShannon Nelson 		break;
699cd552cb4SShannon Nelson 
7000fdd052cSShannon Nelson 	case I40E_NVMUPD_CSUM_LCB:
7010fdd052cSShannon Nelson 		status = i40e_update_nvm_checksum(hw);
7020fdd052cSShannon Nelson 		if (status)
7030fdd052cSShannon Nelson 			*errno = hw->aq.asq_last_status ?
7040fdd052cSShannon Nelson 				   i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
7050fdd052cSShannon Nelson 				   -EIO;
7060fdd052cSShannon Nelson 		else
7070fdd052cSShannon Nelson 			hw->aq.nvm_release_on_done = true;
7080fdd052cSShannon Nelson 		hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
7090fdd052cSShannon Nelson 		break;
7100fdd052cSShannon Nelson 
711cd552cb4SShannon Nelson 	default:
71274d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
71374d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in writing state.\n",
71474d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
715cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
716cd552cb4SShannon Nelson 		*errno = -ESRCH;
717cd552cb4SShannon Nelson 		break;
718cd552cb4SShannon Nelson 	}
719cd552cb4SShannon Nelson 	return status;
720cd552cb4SShannon Nelson }
721cd552cb4SShannon Nelson 
722cd552cb4SShannon Nelson /**
723cd552cb4SShannon Nelson  * i40e_nvmupd_validate_command - Validate given command
724cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
725cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
726cd552cb4SShannon Nelson  * @errno: pointer to return error code
727cd552cb4SShannon Nelson  *
728cd552cb4SShannon Nelson  * Return one of the valid command types or I40E_NVMUPD_INVALID
729cd552cb4SShannon Nelson  **/
730cd552cb4SShannon Nelson static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
731cd552cb4SShannon Nelson 						 struct i40e_nvm_access *cmd,
732cd552cb4SShannon Nelson 						 int *errno)
733cd552cb4SShannon Nelson {
734cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
735cd552cb4SShannon Nelson 	u8 transaction, module;
736cd552cb4SShannon Nelson 
737cd552cb4SShannon Nelson 	/* anything that doesn't match a recognized case is an error */
738cd552cb4SShannon Nelson 	upd_cmd = I40E_NVMUPD_INVALID;
739cd552cb4SShannon Nelson 
740cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
741cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
742cd552cb4SShannon Nelson 
743cd552cb4SShannon Nelson 	/* limits on data size */
744cd552cb4SShannon Nelson 	if ((cmd->data_size < 1) ||
745cd552cb4SShannon Nelson 	    (cmd->data_size > I40E_NVMUPD_MAX_DATA)) {
74674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
74774d0d0edSShannon Nelson 			   "i40e_nvmupd_validate_command data_size %d\n",
748cd552cb4SShannon Nelson 			   cmd->data_size);
749cd552cb4SShannon Nelson 		*errno = -EFAULT;
750cd552cb4SShannon Nelson 		return I40E_NVMUPD_INVALID;
751cd552cb4SShannon Nelson 	}
752cd552cb4SShannon Nelson 
753cd552cb4SShannon Nelson 	switch (cmd->command) {
754cd552cb4SShannon Nelson 	case I40E_NVM_READ:
755cd552cb4SShannon Nelson 		switch (transaction) {
756cd552cb4SShannon Nelson 		case I40E_NVM_CON:
757cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_CON;
758cd552cb4SShannon Nelson 			break;
759cd552cb4SShannon Nelson 		case I40E_NVM_SNT:
760cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_SNT;
761cd552cb4SShannon Nelson 			break;
762cd552cb4SShannon Nelson 		case I40E_NVM_LCB:
763cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_LCB;
764cd552cb4SShannon Nelson 			break;
765cd552cb4SShannon Nelson 		case I40E_NVM_SA:
766cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_SA;
767cd552cb4SShannon Nelson 			break;
768cd552cb4SShannon Nelson 		}
769cd552cb4SShannon Nelson 		break;
770cd552cb4SShannon Nelson 
771cd552cb4SShannon Nelson 	case I40E_NVM_WRITE:
772cd552cb4SShannon Nelson 		switch (transaction) {
773cd552cb4SShannon Nelson 		case I40E_NVM_CON:
774cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_CON;
775cd552cb4SShannon Nelson 			break;
776cd552cb4SShannon Nelson 		case I40E_NVM_SNT:
777cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_SNT;
778cd552cb4SShannon Nelson 			break;
779cd552cb4SShannon Nelson 		case I40E_NVM_LCB:
780cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_LCB;
781cd552cb4SShannon Nelson 			break;
782cd552cb4SShannon Nelson 		case I40E_NVM_SA:
783cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_SA;
784cd552cb4SShannon Nelson 			break;
785cd552cb4SShannon Nelson 		case I40E_NVM_ERA:
786cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_ERA;
787cd552cb4SShannon Nelson 			break;
788cd552cb4SShannon Nelson 		case I40E_NVM_CSUM:
789cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_CON;
790cd552cb4SShannon Nelson 			break;
791cd552cb4SShannon Nelson 		case (I40E_NVM_CSUM|I40E_NVM_SA):
792cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_SA;
793cd552cb4SShannon Nelson 			break;
794cd552cb4SShannon Nelson 		case (I40E_NVM_CSUM|I40E_NVM_LCB):
795cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_LCB;
796cd552cb4SShannon Nelson 			break;
797cd552cb4SShannon Nelson 		}
798cd552cb4SShannon Nelson 		break;
799cd552cb4SShannon Nelson 	}
80074d0d0edSShannon Nelson 	i40e_debug(hw, I40E_DEBUG_NVM, "%s\n",
80174d0d0edSShannon Nelson 		   i40e_nvm_update_state_str[upd_cmd]);
802cd552cb4SShannon Nelson 
803cd552cb4SShannon Nelson 	if (upd_cmd == I40E_NVMUPD_INVALID) {
804cd552cb4SShannon Nelson 		*errno = -EFAULT;
80574d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
80674d0d0edSShannon Nelson 			   "i40e_nvmupd_validate_command returns %d errno %d\n",
807cd552cb4SShannon Nelson 			   upd_cmd, *errno);
808cd552cb4SShannon Nelson 	}
809cd552cb4SShannon Nelson 	return upd_cmd;
810cd552cb4SShannon Nelson }
811cd552cb4SShannon Nelson 
812cd552cb4SShannon Nelson /**
813cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_read - Read NVM
814cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
815cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
816cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
817cd552cb4SShannon Nelson  * @errno: pointer to return error code
818cd552cb4SShannon Nelson  *
819cd552cb4SShannon Nelson  * cmd structure contains identifiers and data buffer
820cd552cb4SShannon Nelson  **/
821cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
822cd552cb4SShannon Nelson 					struct i40e_nvm_access *cmd,
823cd552cb4SShannon Nelson 					u8 *bytes, int *errno)
824cd552cb4SShannon Nelson {
825cd552cb4SShannon Nelson 	i40e_status status;
826cd552cb4SShannon Nelson 	u8 module, transaction;
827cd552cb4SShannon Nelson 	bool last;
828cd552cb4SShannon Nelson 
829cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
830cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
831cd552cb4SShannon Nelson 	last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA);
832cd552cb4SShannon Nelson 
833cd552cb4SShannon Nelson 	status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
834cd552cb4SShannon Nelson 				  bytes, last, NULL);
83574d0d0edSShannon Nelson 	if (status) {
83674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
83774d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_read mod 0x%x  off 0x%x  len 0x%x\n",
83874d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
83974d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
84074d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_read status %d aq %d\n",
84174d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
842cd552cb4SShannon Nelson 		*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
84374d0d0edSShannon Nelson 	}
844cd552cb4SShannon Nelson 
845cd552cb4SShannon Nelson 	return status;
846cd552cb4SShannon Nelson }
847cd552cb4SShannon Nelson 
848cd552cb4SShannon Nelson /**
849cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_erase - Erase an NVM module
850cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
851cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
852cd552cb4SShannon Nelson  * @errno: pointer to return error code
853cd552cb4SShannon Nelson  *
854cd552cb4SShannon Nelson  * module, offset, data_size and data are in cmd structure
855cd552cb4SShannon Nelson  **/
856cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
857cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
858cd552cb4SShannon Nelson 					 int *errno)
859cd552cb4SShannon Nelson {
860cd552cb4SShannon Nelson 	i40e_status status = 0;
861cd552cb4SShannon Nelson 	u8 module, transaction;
862cd552cb4SShannon Nelson 	bool last;
863cd552cb4SShannon Nelson 
864cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
865cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
866cd552cb4SShannon Nelson 	last = (transaction & I40E_NVM_LCB);
867cd552cb4SShannon Nelson 	status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
868cd552cb4SShannon Nelson 				   last, NULL);
86974d0d0edSShannon Nelson 	if (status) {
87074d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
87174d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_erase mod 0x%x  off 0x%x len 0x%x\n",
87274d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
87374d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
87474d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_erase status %d aq %d\n",
87574d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
876cd552cb4SShannon Nelson 		*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
87774d0d0edSShannon Nelson 	}
878cd552cb4SShannon Nelson 
879cd552cb4SShannon Nelson 	return status;
880cd552cb4SShannon Nelson }
881cd552cb4SShannon Nelson 
882cd552cb4SShannon Nelson /**
883cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_write - Write NVM
884cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
885cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
886cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
887cd552cb4SShannon Nelson  * @errno: pointer to return error code
888cd552cb4SShannon Nelson  *
889cd552cb4SShannon Nelson  * module, offset, data_size and data are in cmd structure
890cd552cb4SShannon Nelson  **/
891cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
892cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
893cd552cb4SShannon Nelson 					 u8 *bytes, int *errno)
894cd552cb4SShannon Nelson {
895cd552cb4SShannon Nelson 	i40e_status status = 0;
896cd552cb4SShannon Nelson 	u8 module, transaction;
897cd552cb4SShannon Nelson 	bool last;
898cd552cb4SShannon Nelson 
899cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
900cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
901cd552cb4SShannon Nelson 	last = (transaction & I40E_NVM_LCB);
90274d0d0edSShannon Nelson 
903cd552cb4SShannon Nelson 	status = i40e_aq_update_nvm(hw, module, cmd->offset,
904cd552cb4SShannon Nelson 				    (u16)cmd->data_size, bytes, last, NULL);
90574d0d0edSShannon Nelson 	if (status) {
90674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
90774d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
90874d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
90974d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
91074d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_write status %d aq %d\n",
91174d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
912cd552cb4SShannon Nelson 		*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
91374d0d0edSShannon Nelson 	}
914cd552cb4SShannon Nelson 
915cd552cb4SShannon Nelson 	return status;
916cd552cb4SShannon Nelson }
917