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 
9656a62fc8SJesse Brandeburg 	if (ret_code) {
973e26186dSShannon Nelson 		/* Poll until the current NVM owner timeouts */
98c509c1deSShannon Nelson 		timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT) + gtime;
9956a62fc8SJesse Brandeburg 		while (gtime < timeout) {
10056a62fc8SJesse Brandeburg 			usleep_range(10000, 20000);
101c509c1deSShannon Nelson 			gtime = rd32(hw, I40E_GLVFGEN_TIMER);
10256a62fc8SJesse Brandeburg 			ret_code = i40e_aq_request_resource(hw,
10356a62fc8SJesse Brandeburg 							I40E_NVM_RESOURCE_ID,
104c509c1deSShannon Nelson 							access, 0, &time_left,
10556a62fc8SJesse Brandeburg 							NULL);
10656a62fc8SJesse Brandeburg 			if (!ret_code) {
10756a62fc8SJesse Brandeburg 				hw->nvm.hw_semaphore_timeout =
108c509c1deSShannon Nelson 					    I40E_MS_TO_GTIME(time_left) + gtime;
10956a62fc8SJesse Brandeburg 				break;
11056a62fc8SJesse Brandeburg 			}
11156a62fc8SJesse Brandeburg 		}
11256a62fc8SJesse Brandeburg 		if (ret_code) {
11356a62fc8SJesse Brandeburg 			hw->nvm.hw_semaphore_timeout = 0;
11474d0d0edSShannon Nelson 			i40e_debug(hw, I40E_DEBUG_NVM,
11574d0d0edSShannon Nelson 				   "NVM acquire timed out, wait %llu ms before trying again.\n",
116c509c1deSShannon Nelson 				   time_left);
11756a62fc8SJesse Brandeburg 		}
11856a62fc8SJesse Brandeburg 	}
11956a62fc8SJesse Brandeburg 
12056a62fc8SJesse Brandeburg i40e_i40e_acquire_nvm_exit:
12156a62fc8SJesse Brandeburg 	return ret_code;
12256a62fc8SJesse Brandeburg }
12356a62fc8SJesse Brandeburg 
12456a62fc8SJesse Brandeburg /**
1253e26186dSShannon Nelson  * i40e_release_nvm - Generic request for releasing the NVM ownership
1263e26186dSShannon Nelson  * @hw: pointer to the HW structure
12756a62fc8SJesse Brandeburg  *
12856a62fc8SJesse Brandeburg  * This function will release NVM resource via the proper Admin Command.
12956a62fc8SJesse Brandeburg  **/
13056a62fc8SJesse Brandeburg void i40e_release_nvm(struct i40e_hw *hw)
13156a62fc8SJesse Brandeburg {
13256a62fc8SJesse Brandeburg 	if (!hw->nvm.blank_nvm_mode)
13356a62fc8SJesse Brandeburg 		i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL);
13456a62fc8SJesse Brandeburg }
13556a62fc8SJesse Brandeburg 
13656a62fc8SJesse Brandeburg /**
1373e26186dSShannon Nelson  * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit
1383e26186dSShannon Nelson  * @hw: pointer to the HW structure
13956a62fc8SJesse Brandeburg  *
14056a62fc8SJesse Brandeburg  * Polls the SRCTL Shadow RAM register done bit.
14156a62fc8SJesse Brandeburg  **/
14256a62fc8SJesse Brandeburg static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw)
14356a62fc8SJesse Brandeburg {
14456a62fc8SJesse Brandeburg 	i40e_status ret_code = I40E_ERR_TIMEOUT;
14556a62fc8SJesse Brandeburg 	u32 srctl, wait_cnt;
14656a62fc8SJesse Brandeburg 
1473e26186dSShannon Nelson 	/* Poll the I40E_GLNVM_SRCTL until the done bit is set */
14856a62fc8SJesse Brandeburg 	for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) {
14956a62fc8SJesse Brandeburg 		srctl = rd32(hw, I40E_GLNVM_SRCTL);
15056a62fc8SJesse Brandeburg 		if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) {
15156a62fc8SJesse Brandeburg 			ret_code = 0;
15256a62fc8SJesse Brandeburg 			break;
15356a62fc8SJesse Brandeburg 		}
15456a62fc8SJesse Brandeburg 		udelay(5);
15556a62fc8SJesse Brandeburg 	}
15656a62fc8SJesse Brandeburg 	if (ret_code == I40E_ERR_TIMEOUT)
15774d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set");
15856a62fc8SJesse Brandeburg 	return ret_code;
15956a62fc8SJesse Brandeburg }
16056a62fc8SJesse Brandeburg 
16156a62fc8SJesse Brandeburg /**
162a4bcfbb7SShannon Nelson  * i40e_read_nvm_word - Reads Shadow RAM
1633e26186dSShannon Nelson  * @hw: pointer to the HW structure
1643e26186dSShannon Nelson  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
1653e26186dSShannon Nelson  * @data: word read from the Shadow RAM
16656a62fc8SJesse Brandeburg  *
1673e26186dSShannon Nelson  * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register.
16856a62fc8SJesse Brandeburg  **/
169a4bcfbb7SShannon Nelson i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
17056a62fc8SJesse Brandeburg 					 u16 *data)
17156a62fc8SJesse Brandeburg {
17256a62fc8SJesse Brandeburg 	i40e_status ret_code = I40E_ERR_TIMEOUT;
17356a62fc8SJesse Brandeburg 	u32 sr_reg;
17456a62fc8SJesse Brandeburg 
17556a62fc8SJesse Brandeburg 	if (offset >= hw->nvm.sr_size) {
17674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
17774d0d0edSShannon Nelson 			   "NVM read error: offset %d beyond Shadow RAM limit %d\n",
17874d0d0edSShannon Nelson 			   offset, hw->nvm.sr_size);
17956a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_PARAM;
18056a62fc8SJesse Brandeburg 		goto read_nvm_exit;
18156a62fc8SJesse Brandeburg 	}
18256a62fc8SJesse Brandeburg 
1833e26186dSShannon Nelson 	/* Poll the done bit first */
18456a62fc8SJesse Brandeburg 	ret_code = i40e_poll_sr_srctl_done_bit(hw);
18556a62fc8SJesse Brandeburg 	if (!ret_code) {
1863e26186dSShannon Nelson 		/* Write the address and start reading */
18756a62fc8SJesse Brandeburg 		sr_reg = (u32)(offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) |
18856a62fc8SJesse Brandeburg 			 (1 << I40E_GLNVM_SRCTL_START_SHIFT);
18956a62fc8SJesse Brandeburg 		wr32(hw, I40E_GLNVM_SRCTL, sr_reg);
19056a62fc8SJesse Brandeburg 
1913e26186dSShannon Nelson 		/* Poll I40E_GLNVM_SRCTL until the done bit is set */
19256a62fc8SJesse Brandeburg 		ret_code = i40e_poll_sr_srctl_done_bit(hw);
19356a62fc8SJesse Brandeburg 		if (!ret_code) {
19456a62fc8SJesse Brandeburg 			sr_reg = rd32(hw, I40E_GLNVM_SRDATA);
19556a62fc8SJesse Brandeburg 			*data = (u16)((sr_reg &
19656a62fc8SJesse Brandeburg 				       I40E_GLNVM_SRDATA_RDDATA_MASK)
19756a62fc8SJesse Brandeburg 				    >> I40E_GLNVM_SRDATA_RDDATA_SHIFT);
19856a62fc8SJesse Brandeburg 		}
19956a62fc8SJesse Brandeburg 	}
20056a62fc8SJesse Brandeburg 	if (ret_code)
20174d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
20274d0d0edSShannon Nelson 			   "NVM read error: Couldn't access Shadow RAM address: 0x%x\n",
20356a62fc8SJesse Brandeburg 			   offset);
20456a62fc8SJesse Brandeburg 
20556a62fc8SJesse Brandeburg read_nvm_exit:
20656a62fc8SJesse Brandeburg 	return ret_code;
20756a62fc8SJesse Brandeburg }
20856a62fc8SJesse Brandeburg 
20956a62fc8SJesse Brandeburg /**
2103e26186dSShannon Nelson  * i40e_read_nvm_buffer - Reads Shadow RAM buffer
2113e26186dSShannon Nelson  * @hw: pointer to the HW structure
21256a62fc8SJesse Brandeburg  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
2133e26186dSShannon Nelson  * @words: (in) number of words to read; (out) number of words actually read
2143e26186dSShannon Nelson  * @data: words read from the Shadow RAM
21556a62fc8SJesse Brandeburg  *
21656a62fc8SJesse Brandeburg  * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
21756a62fc8SJesse Brandeburg  * method. The buffer read is preceded by the NVM ownership take
21856a62fc8SJesse Brandeburg  * and followed by the release.
21956a62fc8SJesse Brandeburg  **/
22056a62fc8SJesse Brandeburg i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
22156a62fc8SJesse Brandeburg 					   u16 *words, u16 *data)
22256a62fc8SJesse Brandeburg {
22356a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
22456a62fc8SJesse Brandeburg 	u16 index, word;
22556a62fc8SJesse Brandeburg 
2263e26186dSShannon Nelson 	/* Loop thru the selected region */
22756a62fc8SJesse Brandeburg 	for (word = 0; word < *words; word++) {
22856a62fc8SJesse Brandeburg 		index = offset + word;
229a4bcfbb7SShannon Nelson 		ret_code = i40e_read_nvm_word(hw, index, &data[word]);
23056a62fc8SJesse Brandeburg 		if (ret_code)
23156a62fc8SJesse Brandeburg 			break;
23256a62fc8SJesse Brandeburg 	}
233a4bcfbb7SShannon Nelson 
2343e26186dSShannon Nelson 	/* Update the number of words read from the Shadow RAM */
23556a62fc8SJesse Brandeburg 	*words = word;
23656a62fc8SJesse Brandeburg 
23756a62fc8SJesse Brandeburg 	return ret_code;
23856a62fc8SJesse Brandeburg }
23956a62fc8SJesse Brandeburg 
24056a62fc8SJesse Brandeburg /**
241cd552cb4SShannon Nelson  * i40e_write_nvm_aq - Writes Shadow RAM.
242cd552cb4SShannon Nelson  * @hw: pointer to the HW structure.
243cd552cb4SShannon Nelson  * @module_pointer: module pointer location in words from the NVM beginning
244cd552cb4SShannon Nelson  * @offset: offset in words from module start
245cd552cb4SShannon Nelson  * @words: number of words to write
246cd552cb4SShannon Nelson  * @data: buffer with words to write to the Shadow RAM
247cd552cb4SShannon Nelson  * @last_command: tells the AdminQ that this is the last command
248cd552cb4SShannon Nelson  *
249cd552cb4SShannon Nelson  * Writes a 16 bit words buffer to the Shadow RAM using the admin command.
250cd552cb4SShannon Nelson  **/
251952d9639SWei Yongjun static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
252cd552cb4SShannon Nelson 				     u32 offset, u16 words, void *data,
253cd552cb4SShannon Nelson 				     bool last_command)
254cd552cb4SShannon Nelson {
255cd552cb4SShannon Nelson 	i40e_status ret_code = I40E_ERR_NVM;
256cd552cb4SShannon Nelson 
257cd552cb4SShannon Nelson 	/* Here we are checking the SR limit only for the flat memory model.
258cd552cb4SShannon Nelson 	 * We cannot do it for the module-based model, as we did not acquire
259cd552cb4SShannon Nelson 	 * the NVM resource yet (we cannot get the module pointer value).
260cd552cb4SShannon Nelson 	 * Firmware will check the module-based model.
261cd552cb4SShannon Nelson 	 */
262cd552cb4SShannon Nelson 	if ((offset + words) > hw->nvm.sr_size)
26374d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
26474d0d0edSShannon Nelson 			   "NVM write error: offset %d beyond Shadow RAM limit %d\n",
26574d0d0edSShannon Nelson 			   (offset + words), hw->nvm.sr_size);
266cd552cb4SShannon Nelson 	else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS)
267cd552cb4SShannon Nelson 		/* We can write only up to 4KB (one sector), in one AQ write */
26874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
26974d0d0edSShannon Nelson 			   "NVM write fail error: tried to write %d words, limit is %d.\n",
27074d0d0edSShannon Nelson 			   words, I40E_SR_SECTOR_SIZE_IN_WORDS);
271cd552cb4SShannon Nelson 	else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS)
272cd552cb4SShannon Nelson 		 != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS))
273cd552cb4SShannon Nelson 		/* A single write cannot spread over two sectors */
27474d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
27574d0d0edSShannon Nelson 			   "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n",
27674d0d0edSShannon Nelson 			   offset, words);
277cd552cb4SShannon Nelson 	else
278cd552cb4SShannon Nelson 		ret_code = i40e_aq_update_nvm(hw, module_pointer,
279cd552cb4SShannon Nelson 					      2 * offset,  /*bytes*/
280cd552cb4SShannon Nelson 					      2 * words,   /*bytes*/
281cd552cb4SShannon Nelson 					      data, last_command, NULL);
282cd552cb4SShannon Nelson 
283cd552cb4SShannon Nelson 	return ret_code;
284cd552cb4SShannon Nelson }
285cd552cb4SShannon Nelson 
286cd552cb4SShannon Nelson /**
28756a62fc8SJesse Brandeburg  * i40e_calc_nvm_checksum - Calculates and returns the checksum
28856a62fc8SJesse Brandeburg  * @hw: pointer to hardware structure
28998d44381SJeff Kirsher  * @checksum: pointer to the checksum
29056a62fc8SJesse Brandeburg  *
2913e26186dSShannon Nelson  * This function calculates SW Checksum that covers the whole 64kB shadow RAM
29256a62fc8SJesse Brandeburg  * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD
29356a62fc8SJesse Brandeburg  * is customer specific and unknown. Therefore, this function skips all maximum
29456a62fc8SJesse Brandeburg  * possible size of VPD (1kB).
29556a62fc8SJesse Brandeburg  **/
29656a62fc8SJesse Brandeburg static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
29756a62fc8SJesse Brandeburg 						    u16 *checksum)
29856a62fc8SJesse Brandeburg {
29956a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
30056a62fc8SJesse Brandeburg 	u16 pcie_alt_module = 0;
30156a62fc8SJesse Brandeburg 	u16 checksum_local = 0;
30256a62fc8SJesse Brandeburg 	u16 vpd_module = 0;
30356a62fc8SJesse Brandeburg 	u16 word = 0;
30456a62fc8SJesse Brandeburg 	u32 i = 0;
30556a62fc8SJesse Brandeburg 
30656a62fc8SJesse Brandeburg 	/* read pointer to VPD area */
307a4bcfbb7SShannon Nelson 	ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module);
30856a62fc8SJesse Brandeburg 	if (ret_code) {
30956a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
31056a62fc8SJesse Brandeburg 		goto i40e_calc_nvm_checksum_exit;
31156a62fc8SJesse Brandeburg 	}
31256a62fc8SJesse Brandeburg 
31356a62fc8SJesse Brandeburg 	/* read pointer to PCIe Alt Auto-load module */
314a4bcfbb7SShannon Nelson 	ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR,
31556a62fc8SJesse Brandeburg 				       &pcie_alt_module);
31656a62fc8SJesse Brandeburg 	if (ret_code) {
31756a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
31856a62fc8SJesse Brandeburg 		goto i40e_calc_nvm_checksum_exit;
31956a62fc8SJesse Brandeburg 	}
32056a62fc8SJesse Brandeburg 
32156a62fc8SJesse Brandeburg 	/* Calculate SW checksum that covers the whole 64kB shadow RAM
32256a62fc8SJesse Brandeburg 	 * except the VPD and PCIe ALT Auto-load modules
32356a62fc8SJesse Brandeburg 	 */
32456a62fc8SJesse Brandeburg 	for (i = 0; i < hw->nvm.sr_size; i++) {
32556a62fc8SJesse Brandeburg 		/* Skip Checksum word */
32656a62fc8SJesse Brandeburg 		if (i == I40E_SR_SW_CHECKSUM_WORD)
32756a62fc8SJesse Brandeburg 			i++;
32856a62fc8SJesse Brandeburg 		/* Skip VPD module (convert byte size to word count) */
32956a62fc8SJesse Brandeburg 		if (i == (u32)vpd_module) {
33056a62fc8SJesse Brandeburg 			i += (I40E_SR_VPD_MODULE_MAX_SIZE / 2);
33156a62fc8SJesse Brandeburg 			if (i >= hw->nvm.sr_size)
33256a62fc8SJesse Brandeburg 				break;
33356a62fc8SJesse Brandeburg 		}
33456a62fc8SJesse Brandeburg 		/* Skip PCIe ALT module (convert byte size to word count) */
33556a62fc8SJesse Brandeburg 		if (i == (u32)pcie_alt_module) {
33656a62fc8SJesse Brandeburg 			i += (I40E_SR_PCIE_ALT_MODULE_MAX_SIZE / 2);
33756a62fc8SJesse Brandeburg 			if (i >= hw->nvm.sr_size)
33856a62fc8SJesse Brandeburg 				break;
33956a62fc8SJesse Brandeburg 		}
34056a62fc8SJesse Brandeburg 
341a4bcfbb7SShannon Nelson 		ret_code = i40e_read_nvm_word(hw, (u16)i, &word);
34256a62fc8SJesse Brandeburg 		if (ret_code) {
34356a62fc8SJesse Brandeburg 			ret_code = I40E_ERR_NVM_CHECKSUM;
34456a62fc8SJesse Brandeburg 			goto i40e_calc_nvm_checksum_exit;
34556a62fc8SJesse Brandeburg 		}
34656a62fc8SJesse Brandeburg 		checksum_local += word;
34756a62fc8SJesse Brandeburg 	}
34856a62fc8SJesse Brandeburg 
34956a62fc8SJesse Brandeburg 	*checksum = (u16)I40E_SR_SW_CHECKSUM_BASE - checksum_local;
35056a62fc8SJesse Brandeburg 
35156a62fc8SJesse Brandeburg i40e_calc_nvm_checksum_exit:
35256a62fc8SJesse Brandeburg 	return ret_code;
35356a62fc8SJesse Brandeburg }
35456a62fc8SJesse Brandeburg 
35556a62fc8SJesse Brandeburg /**
356cd552cb4SShannon Nelson  * i40e_update_nvm_checksum - Updates the NVM checksum
357cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
358cd552cb4SShannon Nelson  *
359cd552cb4SShannon Nelson  * NVM ownership must be acquired before calling this function and released
360cd552cb4SShannon Nelson  * on ARQ completion event reception by caller.
361cd552cb4SShannon Nelson  * This function will commit SR to NVM.
362cd552cb4SShannon Nelson  **/
363cd552cb4SShannon Nelson i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw)
364cd552cb4SShannon Nelson {
365cd552cb4SShannon Nelson 	i40e_status ret_code = 0;
366cd552cb4SShannon Nelson 	u16 checksum;
367cd552cb4SShannon Nelson 
368cd552cb4SShannon Nelson 	ret_code = i40e_calc_nvm_checksum(hw, &checksum);
369cd552cb4SShannon Nelson 	if (!ret_code)
370cd552cb4SShannon Nelson 		ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD,
371cd552cb4SShannon Nelson 					     1, &checksum, true);
372cd552cb4SShannon Nelson 
373cd552cb4SShannon Nelson 	return ret_code;
374cd552cb4SShannon Nelson }
375cd552cb4SShannon Nelson 
376cd552cb4SShannon Nelson /**
37756a62fc8SJesse Brandeburg  * i40e_validate_nvm_checksum - Validate EEPROM checksum
37856a62fc8SJesse Brandeburg  * @hw: pointer to hardware structure
37956a62fc8SJesse Brandeburg  * @checksum: calculated checksum
38056a62fc8SJesse Brandeburg  *
38156a62fc8SJesse Brandeburg  * Performs checksum calculation and validates the NVM SW checksum. If the
38256a62fc8SJesse Brandeburg  * caller does not need checksum, the value can be NULL.
38356a62fc8SJesse Brandeburg  **/
38456a62fc8SJesse Brandeburg i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
38556a62fc8SJesse Brandeburg 						 u16 *checksum)
38656a62fc8SJesse Brandeburg {
38756a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
38856a62fc8SJesse Brandeburg 	u16 checksum_sr = 0;
389e15c9fa0SJesse Brandeburg 	u16 checksum_local = 0;
39056a62fc8SJesse Brandeburg 
39156a62fc8SJesse Brandeburg 	ret_code = i40e_calc_nvm_checksum(hw, &checksum_local);
39256a62fc8SJesse Brandeburg 	if (ret_code)
3937a208e83SKamil Krawczyk 		goto i40e_validate_nvm_checksum_exit;
39456a62fc8SJesse Brandeburg 
39556a62fc8SJesse Brandeburg 	/* Do not use i40e_read_nvm_word() because we do not want to take
39656a62fc8SJesse Brandeburg 	 * the synchronization semaphores twice here.
39756a62fc8SJesse Brandeburg 	 */
398a4bcfbb7SShannon Nelson 	i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr);
39956a62fc8SJesse Brandeburg 
40056a62fc8SJesse Brandeburg 	/* Verify read checksum from EEPROM is the same as
40156a62fc8SJesse Brandeburg 	 * calculated checksum
40256a62fc8SJesse Brandeburg 	 */
40356a62fc8SJesse Brandeburg 	if (checksum_local != checksum_sr)
40456a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
40556a62fc8SJesse Brandeburg 
40656a62fc8SJesse Brandeburg 	/* If the user cares, return the calculated checksum */
40756a62fc8SJesse Brandeburg 	if (checksum)
40856a62fc8SJesse Brandeburg 		*checksum = checksum_local;
40956a62fc8SJesse Brandeburg 
41056a62fc8SJesse Brandeburg i40e_validate_nvm_checksum_exit:
41156a62fc8SJesse Brandeburg 	return ret_code;
41256a62fc8SJesse Brandeburg }
413cd552cb4SShannon Nelson 
414cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
415cd552cb4SShannon Nelson 					  struct i40e_nvm_access *cmd,
416cd552cb4SShannon Nelson 					  u8 *bytes, int *errno);
417cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
418cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
419cd552cb4SShannon Nelson 					     u8 *bytes, int *errno);
420cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
421cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
422cd552cb4SShannon Nelson 					     u8 *bytes, int *errno);
423cd552cb4SShannon Nelson static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
424cd552cb4SShannon Nelson 						struct i40e_nvm_access *cmd,
425cd552cb4SShannon Nelson 						int *errno);
426cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
427cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
428cd552cb4SShannon Nelson 					 int *errno);
429cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
430cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
431cd552cb4SShannon Nelson 					 u8 *bytes, int *errno);
432cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
433cd552cb4SShannon Nelson 					struct i40e_nvm_access *cmd,
434cd552cb4SShannon Nelson 					u8 *bytes, int *errno);
435cd552cb4SShannon Nelson static inline u8 i40e_nvmupd_get_module(u32 val)
436cd552cb4SShannon Nelson {
437cd552cb4SShannon Nelson 	return (u8)(val & I40E_NVM_MOD_PNT_MASK);
438cd552cb4SShannon Nelson }
439cd552cb4SShannon Nelson static inline u8 i40e_nvmupd_get_transaction(u32 val)
440cd552cb4SShannon Nelson {
441cd552cb4SShannon Nelson 	return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT);
442cd552cb4SShannon Nelson }
443cd552cb4SShannon Nelson 
44474d0d0edSShannon Nelson static char *i40e_nvm_update_state_str[] = {
44574d0d0edSShannon Nelson 	"I40E_NVMUPD_INVALID",
44674d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_CON",
44774d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_SNT",
44874d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_LCB",
44974d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_SA",
45074d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_ERA",
45174d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_CON",
45274d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_SNT",
45374d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_LCB",
45474d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_SA",
45574d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_CON",
45674d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_SA",
45774d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_LCB",
45874d0d0edSShannon Nelson };
45974d0d0edSShannon Nelson 
460cd552cb4SShannon Nelson /**
461cd552cb4SShannon Nelson  * i40e_nvmupd_command - Process an NVM update command
462cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
463cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command
464cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
465cd552cb4SShannon Nelson  * @errno: pointer to return error code
466cd552cb4SShannon Nelson  *
467cd552cb4SShannon Nelson  * Dispatches command depending on what update state is current
468cd552cb4SShannon Nelson  **/
469cd552cb4SShannon Nelson i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
470cd552cb4SShannon Nelson 				struct i40e_nvm_access *cmd,
471cd552cb4SShannon Nelson 				u8 *bytes, int *errno)
472cd552cb4SShannon Nelson {
473cd552cb4SShannon Nelson 	i40e_status status;
474cd552cb4SShannon Nelson 
475cd552cb4SShannon Nelson 	/* assume success */
476cd552cb4SShannon Nelson 	*errno = 0;
477cd552cb4SShannon Nelson 
478cd552cb4SShannon Nelson 	switch (hw->nvmupd_state) {
479cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_INIT:
480cd552cb4SShannon Nelson 		status = i40e_nvmupd_state_init(hw, cmd, bytes, errno);
481cd552cb4SShannon Nelson 		break;
482cd552cb4SShannon Nelson 
483cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_READING:
484cd552cb4SShannon Nelson 		status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno);
485cd552cb4SShannon Nelson 		break;
486cd552cb4SShannon Nelson 
487cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_WRITING:
488cd552cb4SShannon Nelson 		status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno);
489cd552cb4SShannon Nelson 		break;
490cd552cb4SShannon Nelson 
491cd552cb4SShannon Nelson 	default:
492cd552cb4SShannon Nelson 		/* invalid state, should never happen */
49374d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
49474d0d0edSShannon Nelson 			   "NVMUPD: no such state %d\n", hw->nvmupd_state);
495cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
496cd552cb4SShannon Nelson 		*errno = -ESRCH;
497cd552cb4SShannon Nelson 		break;
498cd552cb4SShannon Nelson 	}
499cd552cb4SShannon Nelson 	return status;
500cd552cb4SShannon Nelson }
501cd552cb4SShannon Nelson 
502cd552cb4SShannon Nelson /**
503cd552cb4SShannon Nelson  * i40e_nvmupd_state_init - Handle NVM update state Init
504cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
505cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
506cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
507cd552cb4SShannon Nelson  * @errno: pointer to return error code
508cd552cb4SShannon Nelson  *
509cd552cb4SShannon Nelson  * Process legitimate commands of the Init state and conditionally set next
510cd552cb4SShannon Nelson  * state. Reject all other commands.
511cd552cb4SShannon Nelson  **/
512cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
513cd552cb4SShannon Nelson 					  struct i40e_nvm_access *cmd,
514cd552cb4SShannon Nelson 					  u8 *bytes, int *errno)
515cd552cb4SShannon Nelson {
516cd552cb4SShannon Nelson 	i40e_status status = 0;
517cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
518cd552cb4SShannon Nelson 
519cd552cb4SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
520cd552cb4SShannon Nelson 
521cd552cb4SShannon Nelson 	switch (upd_cmd) {
522cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SA:
523cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
524cd552cb4SShannon Nelson 		if (status) {
525cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
526cd552cb4SShannon Nelson 		} else {
527cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
528cd552cb4SShannon Nelson 			i40e_release_nvm(hw);
529cd552cb4SShannon Nelson 		}
530cd552cb4SShannon Nelson 		break;
531cd552cb4SShannon Nelson 
532cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SNT:
533cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
534cd552cb4SShannon Nelson 		if (status) {
535cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
536cd552cb4SShannon Nelson 		} else {
537cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
538cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_READING;
539cd552cb4SShannon Nelson 		}
540cd552cb4SShannon Nelson 		break;
541cd552cb4SShannon Nelson 
542cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_ERA:
543cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
544cd552cb4SShannon Nelson 		if (status) {
545cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
546cd552cb4SShannon Nelson 		} else {
547cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_erase(hw, cmd, errno);
548cd552cb4SShannon Nelson 			if (status)
549cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
550cd552cb4SShannon Nelson 			else
551cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
552cd552cb4SShannon Nelson 		}
553cd552cb4SShannon Nelson 		break;
554cd552cb4SShannon Nelson 
555cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_SA:
556cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
557cd552cb4SShannon Nelson 		if (status) {
558cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
559cd552cb4SShannon Nelson 		} else {
560cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
561cd552cb4SShannon Nelson 			if (status)
562cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
563cd552cb4SShannon Nelson 			else
564cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
565cd552cb4SShannon Nelson 		}
566cd552cb4SShannon Nelson 		break;
567cd552cb4SShannon Nelson 
568cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_SNT:
569cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
570cd552cb4SShannon Nelson 		if (status) {
571cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
572cd552cb4SShannon Nelson 		} else {
573cd552cb4SShannon Nelson 			status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
574cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING;
575cd552cb4SShannon Nelson 		}
576cd552cb4SShannon Nelson 		break;
577cd552cb4SShannon Nelson 
578cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_SA:
579cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
580cd552cb4SShannon Nelson 		if (status) {
581cd552cb4SShannon Nelson 			*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
582cd552cb4SShannon Nelson 		} else {
583cd552cb4SShannon Nelson 			status = i40e_update_nvm_checksum(hw);
584cd552cb4SShannon Nelson 			if (status) {
585cd552cb4SShannon Nelson 				*errno = hw->aq.asq_last_status ?
586cd552cb4SShannon Nelson 				   i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
587cd552cb4SShannon Nelson 				   -EIO;
588cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
589cd552cb4SShannon Nelson 			} else {
590cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
591cd552cb4SShannon Nelson 			}
592cd552cb4SShannon Nelson 		}
593cd552cb4SShannon Nelson 		break;
594cd552cb4SShannon Nelson 
595cd552cb4SShannon Nelson 	default:
59674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
59774d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in init state\n",
59874d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
599cd552cb4SShannon Nelson 		status = I40E_ERR_NVM;
600cd552cb4SShannon Nelson 		*errno = -ESRCH;
601cd552cb4SShannon Nelson 		break;
602cd552cb4SShannon Nelson 	}
603cd552cb4SShannon Nelson 	return status;
604cd552cb4SShannon Nelson }
605cd552cb4SShannon Nelson 
606cd552cb4SShannon Nelson /**
607cd552cb4SShannon Nelson  * i40e_nvmupd_state_reading - Handle NVM update state Reading
608cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
609cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
610cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
611cd552cb4SShannon Nelson  * @errno: pointer to return error code
612cd552cb4SShannon Nelson  *
613cd552cb4SShannon Nelson  * NVM ownership is already held.  Process legitimate commands and set any
614cd552cb4SShannon Nelson  * change in state; reject all other commands.
615cd552cb4SShannon Nelson  **/
616cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
617cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
618cd552cb4SShannon Nelson 					     u8 *bytes, int *errno)
619cd552cb4SShannon Nelson {
620cd552cb4SShannon Nelson 	i40e_status status;
621cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
622cd552cb4SShannon Nelson 
623cd552cb4SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
624cd552cb4SShannon Nelson 
625cd552cb4SShannon Nelson 	switch (upd_cmd) {
626cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SA:
627cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_CON:
628cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
629cd552cb4SShannon Nelson 		break;
630cd552cb4SShannon Nelson 
631cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_LCB:
632cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
633cd552cb4SShannon Nelson 		i40e_release_nvm(hw);
634cd552cb4SShannon Nelson 		hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
635cd552cb4SShannon Nelson 		break;
636cd552cb4SShannon Nelson 
637cd552cb4SShannon Nelson 	default:
63874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
63974d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in reading state.\n",
64074d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
641cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
642cd552cb4SShannon Nelson 		*errno = -ESRCH;
643cd552cb4SShannon Nelson 		break;
644cd552cb4SShannon Nelson 	}
645cd552cb4SShannon Nelson 	return status;
646cd552cb4SShannon Nelson }
647cd552cb4SShannon Nelson 
648cd552cb4SShannon Nelson /**
649cd552cb4SShannon Nelson  * i40e_nvmupd_state_writing - Handle NVM update state Writing
650cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
651cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
652cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
653cd552cb4SShannon Nelson  * @errno: pointer to return error code
654cd552cb4SShannon Nelson  *
655cd552cb4SShannon Nelson  * NVM ownership is already held.  Process legitimate commands and set any
656cd552cb4SShannon Nelson  * change in state; reject all other commands
657cd552cb4SShannon Nelson  **/
658cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
659cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
660cd552cb4SShannon Nelson 					     u8 *bytes, int *errno)
661cd552cb4SShannon Nelson {
662cd552cb4SShannon Nelson 	i40e_status status;
663cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
664cd552cb4SShannon Nelson 
665cd552cb4SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
666cd552cb4SShannon Nelson 
667cd552cb4SShannon Nelson 	switch (upd_cmd) {
668cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_CON:
669cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
670cd552cb4SShannon Nelson 		break;
671cd552cb4SShannon Nelson 
672cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_LCB:
673cd552cb4SShannon Nelson 		status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
674cd552cb4SShannon Nelson 		if (!status) {
675cd552cb4SShannon Nelson 			hw->aq.nvm_release_on_done = true;
676cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
677cd552cb4SShannon Nelson 		}
678cd552cb4SShannon Nelson 		break;
679cd552cb4SShannon Nelson 
680cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_CON:
681cd552cb4SShannon Nelson 		status = i40e_update_nvm_checksum(hw);
682cd552cb4SShannon Nelson 		if (status)
683cd552cb4SShannon Nelson 			*errno = hw->aq.asq_last_status ?
684cd552cb4SShannon Nelson 				   i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
685cd552cb4SShannon Nelson 				   -EIO;
686cd552cb4SShannon Nelson 		break;
687cd552cb4SShannon Nelson 
688cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_LCB:
689cd552cb4SShannon Nelson 		status = i40e_update_nvm_checksum(hw);
690cd552cb4SShannon Nelson 		if (status) {
691cd552cb4SShannon Nelson 			*errno = hw->aq.asq_last_status ?
692cd552cb4SShannon Nelson 				   i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
693cd552cb4SShannon Nelson 				   -EIO;
694cd552cb4SShannon Nelson 		} else {
695cd552cb4SShannon Nelson 			hw->aq.nvm_release_on_done = true;
696cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
697cd552cb4SShannon Nelson 		}
698cd552cb4SShannon Nelson 		break;
699cd552cb4SShannon Nelson 
700cd552cb4SShannon Nelson 	default:
70174d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
70274d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in writing state.\n",
70374d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
704cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
705cd552cb4SShannon Nelson 		*errno = -ESRCH;
706cd552cb4SShannon Nelson 		break;
707cd552cb4SShannon Nelson 	}
708cd552cb4SShannon Nelson 	return status;
709cd552cb4SShannon Nelson }
710cd552cb4SShannon Nelson 
711cd552cb4SShannon Nelson /**
712cd552cb4SShannon Nelson  * i40e_nvmupd_validate_command - Validate given command
713cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
714cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
715cd552cb4SShannon Nelson  * @errno: pointer to return error code
716cd552cb4SShannon Nelson  *
717cd552cb4SShannon Nelson  * Return one of the valid command types or I40E_NVMUPD_INVALID
718cd552cb4SShannon Nelson  **/
719cd552cb4SShannon Nelson static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
720cd552cb4SShannon Nelson 						 struct i40e_nvm_access *cmd,
721cd552cb4SShannon Nelson 						 int *errno)
722cd552cb4SShannon Nelson {
723cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
724cd552cb4SShannon Nelson 	u8 transaction, module;
725cd552cb4SShannon Nelson 
726cd552cb4SShannon Nelson 	/* anything that doesn't match a recognized case is an error */
727cd552cb4SShannon Nelson 	upd_cmd = I40E_NVMUPD_INVALID;
728cd552cb4SShannon Nelson 
729cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
730cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
731cd552cb4SShannon Nelson 
732cd552cb4SShannon Nelson 	/* limits on data size */
733cd552cb4SShannon Nelson 	if ((cmd->data_size < 1) ||
734cd552cb4SShannon Nelson 	    (cmd->data_size > I40E_NVMUPD_MAX_DATA)) {
73574d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
73674d0d0edSShannon Nelson 			   "i40e_nvmupd_validate_command data_size %d\n",
737cd552cb4SShannon Nelson 			   cmd->data_size);
738cd552cb4SShannon Nelson 		*errno = -EFAULT;
739cd552cb4SShannon Nelson 		return I40E_NVMUPD_INVALID;
740cd552cb4SShannon Nelson 	}
741cd552cb4SShannon Nelson 
742cd552cb4SShannon Nelson 	switch (cmd->command) {
743cd552cb4SShannon Nelson 	case I40E_NVM_READ:
744cd552cb4SShannon Nelson 		switch (transaction) {
745cd552cb4SShannon Nelson 		case I40E_NVM_CON:
746cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_CON;
747cd552cb4SShannon Nelson 			break;
748cd552cb4SShannon Nelson 		case I40E_NVM_SNT:
749cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_SNT;
750cd552cb4SShannon Nelson 			break;
751cd552cb4SShannon Nelson 		case I40E_NVM_LCB:
752cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_LCB;
753cd552cb4SShannon Nelson 			break;
754cd552cb4SShannon Nelson 		case I40E_NVM_SA:
755cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_SA;
756cd552cb4SShannon Nelson 			break;
757cd552cb4SShannon Nelson 		}
758cd552cb4SShannon Nelson 		break;
759cd552cb4SShannon Nelson 
760cd552cb4SShannon Nelson 	case I40E_NVM_WRITE:
761cd552cb4SShannon Nelson 		switch (transaction) {
762cd552cb4SShannon Nelson 		case I40E_NVM_CON:
763cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_CON;
764cd552cb4SShannon Nelson 			break;
765cd552cb4SShannon Nelson 		case I40E_NVM_SNT:
766cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_SNT;
767cd552cb4SShannon Nelson 			break;
768cd552cb4SShannon Nelson 		case I40E_NVM_LCB:
769cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_LCB;
770cd552cb4SShannon Nelson 			break;
771cd552cb4SShannon Nelson 		case I40E_NVM_SA:
772cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_SA;
773cd552cb4SShannon Nelson 			break;
774cd552cb4SShannon Nelson 		case I40E_NVM_ERA:
775cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_ERA;
776cd552cb4SShannon Nelson 			break;
777cd552cb4SShannon Nelson 		case I40E_NVM_CSUM:
778cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_CON;
779cd552cb4SShannon Nelson 			break;
780cd552cb4SShannon Nelson 		case (I40E_NVM_CSUM|I40E_NVM_SA):
781cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_SA;
782cd552cb4SShannon Nelson 			break;
783cd552cb4SShannon Nelson 		case (I40E_NVM_CSUM|I40E_NVM_LCB):
784cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_LCB;
785cd552cb4SShannon Nelson 			break;
786cd552cb4SShannon Nelson 		}
787cd552cb4SShannon Nelson 		break;
788cd552cb4SShannon Nelson 	}
78974d0d0edSShannon Nelson 	i40e_debug(hw, I40E_DEBUG_NVM, "%s\n",
79074d0d0edSShannon Nelson 		   i40e_nvm_update_state_str[upd_cmd]);
791cd552cb4SShannon Nelson 
792cd552cb4SShannon Nelson 	if (upd_cmd == I40E_NVMUPD_INVALID) {
793cd552cb4SShannon Nelson 		*errno = -EFAULT;
79474d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
79574d0d0edSShannon Nelson 			   "i40e_nvmupd_validate_command returns %d errno %d\n",
796cd552cb4SShannon Nelson 			   upd_cmd, *errno);
797cd552cb4SShannon Nelson 	}
798cd552cb4SShannon Nelson 	return upd_cmd;
799cd552cb4SShannon Nelson }
800cd552cb4SShannon Nelson 
801cd552cb4SShannon Nelson /**
802cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_read - Read NVM
803cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
804cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
805cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
806cd552cb4SShannon Nelson  * @errno: pointer to return error code
807cd552cb4SShannon Nelson  *
808cd552cb4SShannon Nelson  * cmd structure contains identifiers and data buffer
809cd552cb4SShannon Nelson  **/
810cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
811cd552cb4SShannon Nelson 					struct i40e_nvm_access *cmd,
812cd552cb4SShannon Nelson 					u8 *bytes, int *errno)
813cd552cb4SShannon Nelson {
814cd552cb4SShannon Nelson 	i40e_status status;
815cd552cb4SShannon Nelson 	u8 module, transaction;
816cd552cb4SShannon Nelson 	bool last;
817cd552cb4SShannon Nelson 
818cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
819cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
820cd552cb4SShannon Nelson 	last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA);
821cd552cb4SShannon Nelson 
822cd552cb4SShannon Nelson 	status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
823cd552cb4SShannon Nelson 				  bytes, last, NULL);
82474d0d0edSShannon Nelson 	if (status) {
82574d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
82674d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_read mod 0x%x  off 0x%x  len 0x%x\n",
82774d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
82874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
82974d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_read status %d aq %d\n",
83074d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
831cd552cb4SShannon Nelson 		*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
83274d0d0edSShannon Nelson 	}
833cd552cb4SShannon Nelson 
834cd552cb4SShannon Nelson 	return status;
835cd552cb4SShannon Nelson }
836cd552cb4SShannon Nelson 
837cd552cb4SShannon Nelson /**
838cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_erase - Erase an NVM module
839cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
840cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
841cd552cb4SShannon Nelson  * @errno: pointer to return error code
842cd552cb4SShannon Nelson  *
843cd552cb4SShannon Nelson  * module, offset, data_size and data are in cmd structure
844cd552cb4SShannon Nelson  **/
845cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
846cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
847cd552cb4SShannon Nelson 					 int *errno)
848cd552cb4SShannon Nelson {
849cd552cb4SShannon Nelson 	i40e_status status = 0;
850cd552cb4SShannon Nelson 	u8 module, transaction;
851cd552cb4SShannon Nelson 	bool last;
852cd552cb4SShannon Nelson 
853cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
854cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
855cd552cb4SShannon Nelson 	last = (transaction & I40E_NVM_LCB);
856cd552cb4SShannon Nelson 	status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
857cd552cb4SShannon Nelson 				   last, NULL);
85874d0d0edSShannon Nelson 	if (status) {
85974d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
86074d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_erase mod 0x%x  off 0x%x len 0x%x\n",
86174d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
86274d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
86374d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_erase status %d aq %d\n",
86474d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
865cd552cb4SShannon Nelson 		*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
86674d0d0edSShannon Nelson 	}
867cd552cb4SShannon Nelson 
868cd552cb4SShannon Nelson 	return status;
869cd552cb4SShannon Nelson }
870cd552cb4SShannon Nelson 
871cd552cb4SShannon Nelson /**
872cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_write - Write NVM
873cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
874cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
875cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
876cd552cb4SShannon Nelson  * @errno: pointer to return error code
877cd552cb4SShannon Nelson  *
878cd552cb4SShannon Nelson  * module, offset, data_size and data are in cmd structure
879cd552cb4SShannon Nelson  **/
880cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
881cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
882cd552cb4SShannon Nelson 					 u8 *bytes, int *errno)
883cd552cb4SShannon Nelson {
884cd552cb4SShannon Nelson 	i40e_status status = 0;
885cd552cb4SShannon Nelson 	u8 module, transaction;
886cd552cb4SShannon Nelson 	bool last;
887cd552cb4SShannon Nelson 
888cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
889cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
890cd552cb4SShannon Nelson 	last = (transaction & I40E_NVM_LCB);
89174d0d0edSShannon Nelson 
892cd552cb4SShannon Nelson 	status = i40e_aq_update_nvm(hw, module, cmd->offset,
893cd552cb4SShannon Nelson 				    (u16)cmd->data_size, bytes, last, NULL);
89474d0d0edSShannon Nelson 	if (status) {
89574d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
89674d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
89774d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
89874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
89974d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_write status %d aq %d\n",
90074d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
901cd552cb4SShannon Nelson 		*errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
90274d0d0edSShannon Nelson 	}
903cd552cb4SShannon Nelson 
904cd552cb4SShannon Nelson 	return status;
905cd552cb4SShannon Nelson }
906