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) */
5341a1d04bSJesse Brandeburg 	nvm->sr_size = BIT(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 /**
167d1bbe0eaSKamil Krawczyk  * i40e_read_nvm_word_srctl - Reads Shadow RAM via SRCTL register
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  **/
17437a2973aSShannon Nelson static i40e_status i40e_read_nvm_word_srctl(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 */
19241a1d04bSJesse Brandeburg 		sr_reg = ((u32)offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) |
19341a1d04bSJesse Brandeburg 			 BIT(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 /**
2157073f46eSShannon Nelson  * i40e_read_nvm_aq - Read Shadow RAM.
2167073f46eSShannon Nelson  * @hw: pointer to the HW structure.
2177073f46eSShannon Nelson  * @module_pointer: module pointer location in words from the NVM beginning
2187073f46eSShannon Nelson  * @offset: offset in words from module start
2197073f46eSShannon Nelson  * @words: number of words to write
2207073f46eSShannon Nelson  * @data: buffer with words to write to the Shadow RAM
2217073f46eSShannon Nelson  * @last_command: tells the AdminQ that this is the last command
2227073f46eSShannon Nelson  *
2237073f46eSShannon Nelson  * Writes a 16 bit words buffer to the Shadow RAM using the admin command.
2247073f46eSShannon Nelson  **/
2257073f46eSShannon Nelson static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
2267073f46eSShannon Nelson 				    u32 offset, u16 words, void *data,
2277073f46eSShannon Nelson 				    bool last_command)
2287073f46eSShannon Nelson {
2297073f46eSShannon Nelson 	i40e_status ret_code = I40E_ERR_NVM;
2307073f46eSShannon Nelson 	struct i40e_asq_cmd_details cmd_details;
2317073f46eSShannon Nelson 
2327073f46eSShannon Nelson 	memset(&cmd_details, 0, sizeof(cmd_details));
2337073f46eSShannon Nelson 
2347073f46eSShannon Nelson 	/* Here we are checking the SR limit only for the flat memory model.
2357073f46eSShannon Nelson 	 * We cannot do it for the module-based model, as we did not acquire
2367073f46eSShannon Nelson 	 * the NVM resource yet (we cannot get the module pointer value).
2377073f46eSShannon Nelson 	 * Firmware will check the module-based model.
2387073f46eSShannon Nelson 	 */
2397073f46eSShannon Nelson 	if ((offset + words) > hw->nvm.sr_size)
2407073f46eSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
2417073f46eSShannon Nelson 			   "NVM write error: offset %d beyond Shadow RAM limit %d\n",
2427073f46eSShannon Nelson 			   (offset + words), hw->nvm.sr_size);
2437073f46eSShannon Nelson 	else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS)
2447073f46eSShannon Nelson 		/* We can write only up to 4KB (one sector), in one AQ write */
2457073f46eSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
2467073f46eSShannon Nelson 			   "NVM write fail error: tried to write %d words, limit is %d.\n",
2477073f46eSShannon Nelson 			   words, I40E_SR_SECTOR_SIZE_IN_WORDS);
2487073f46eSShannon Nelson 	else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS)
2497073f46eSShannon Nelson 		 != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS))
2507073f46eSShannon Nelson 		/* A single write cannot spread over two sectors */
2517073f46eSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
2527073f46eSShannon Nelson 			   "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n",
2537073f46eSShannon Nelson 			   offset, words);
2547073f46eSShannon Nelson 	else
2557073f46eSShannon Nelson 		ret_code = i40e_aq_read_nvm(hw, module_pointer,
2567073f46eSShannon Nelson 					    2 * offset,  /*bytes*/
2577073f46eSShannon Nelson 					    2 * words,   /*bytes*/
2587073f46eSShannon Nelson 					    data, last_command, &cmd_details);
2597073f46eSShannon Nelson 
2607073f46eSShannon Nelson 	return ret_code;
2617073f46eSShannon Nelson }
2627073f46eSShannon Nelson 
2637073f46eSShannon Nelson /**
2647073f46eSShannon Nelson  * i40e_read_nvm_word_aq - Reads Shadow RAM via AQ
2657073f46eSShannon Nelson  * @hw: pointer to the HW structure
2667073f46eSShannon Nelson  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
2677073f46eSShannon Nelson  * @data: word read from the Shadow RAM
2687073f46eSShannon Nelson  *
2697073f46eSShannon Nelson  * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register.
2707073f46eSShannon Nelson  **/
2717073f46eSShannon Nelson static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset,
2727073f46eSShannon Nelson 					 u16 *data)
2737073f46eSShannon Nelson {
2747073f46eSShannon Nelson 	i40e_status ret_code = I40E_ERR_TIMEOUT;
2757073f46eSShannon Nelson 
2767073f46eSShannon Nelson 	ret_code = i40e_read_nvm_aq(hw, 0x0, offset, 1, data, true);
2777073f46eSShannon Nelson 	*data = le16_to_cpu(*(__le16 *)data);
2787073f46eSShannon Nelson 
2797073f46eSShannon Nelson 	return ret_code;
2807073f46eSShannon Nelson }
2817073f46eSShannon Nelson 
2827073f46eSShannon Nelson /**
283d1bbe0eaSKamil Krawczyk  * i40e_read_nvm_word - Reads Shadow RAM
284d1bbe0eaSKamil Krawczyk  * @hw: pointer to the HW structure
285d1bbe0eaSKamil Krawczyk  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
286d1bbe0eaSKamil Krawczyk  * @data: word read from the Shadow RAM
287d1bbe0eaSKamil Krawczyk  *
288d1bbe0eaSKamil Krawczyk  * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register.
289d1bbe0eaSKamil Krawczyk  **/
290d1bbe0eaSKamil Krawczyk i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
291d1bbe0eaSKamil Krawczyk 			       u16 *data)
292d1bbe0eaSKamil Krawczyk {
2937073f46eSShannon Nelson 	if (hw->mac.type == I40E_MAC_X722)
2947073f46eSShannon Nelson 		return i40e_read_nvm_word_aq(hw, offset, data);
295d1bbe0eaSKamil Krawczyk 	return i40e_read_nvm_word_srctl(hw, offset, data);
296d1bbe0eaSKamil Krawczyk }
297d1bbe0eaSKamil Krawczyk 
298d1bbe0eaSKamil Krawczyk /**
299d1bbe0eaSKamil Krawczyk  * i40e_read_nvm_buffer_srctl - Reads Shadow RAM buffer via SRCTL register
300d1bbe0eaSKamil Krawczyk  * @hw: pointer to the HW structure
301d1bbe0eaSKamil Krawczyk  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
302d1bbe0eaSKamil Krawczyk  * @words: (in) number of words to read; (out) number of words actually read
303d1bbe0eaSKamil Krawczyk  * @data: words read from the Shadow RAM
304d1bbe0eaSKamil Krawczyk  *
305d1bbe0eaSKamil Krawczyk  * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
306d1bbe0eaSKamil Krawczyk  * method. The buffer read is preceded by the NVM ownership take
307d1bbe0eaSKamil Krawczyk  * and followed by the release.
308d1bbe0eaSKamil Krawczyk  **/
30937a2973aSShannon Nelson static i40e_status i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset,
310d1bbe0eaSKamil Krawczyk 					      u16 *words, u16 *data)
311d1bbe0eaSKamil Krawczyk {
312d1bbe0eaSKamil Krawczyk 	i40e_status ret_code = 0;
313d1bbe0eaSKamil Krawczyk 	u16 index, word;
314d1bbe0eaSKamil Krawczyk 
315d1bbe0eaSKamil Krawczyk 	/* Loop thru the selected region */
316d1bbe0eaSKamil Krawczyk 	for (word = 0; word < *words; word++) {
317d1bbe0eaSKamil Krawczyk 		index = offset + word;
318d1bbe0eaSKamil Krawczyk 		ret_code = i40e_read_nvm_word_srctl(hw, index, &data[word]);
319d1bbe0eaSKamil Krawczyk 		if (ret_code)
320d1bbe0eaSKamil Krawczyk 			break;
321d1bbe0eaSKamil Krawczyk 	}
322d1bbe0eaSKamil Krawczyk 
323d1bbe0eaSKamil Krawczyk 	/* Update the number of words read from the Shadow RAM */
324d1bbe0eaSKamil Krawczyk 	*words = word;
325d1bbe0eaSKamil Krawczyk 
326d1bbe0eaSKamil Krawczyk 	return ret_code;
327d1bbe0eaSKamil Krawczyk }
328d1bbe0eaSKamil Krawczyk 
329d1bbe0eaSKamil Krawczyk /**
3307073f46eSShannon Nelson  * i40e_read_nvm_buffer_aq - Reads Shadow RAM buffer via AQ
3317073f46eSShannon Nelson  * @hw: pointer to the HW structure
3327073f46eSShannon Nelson  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
3337073f46eSShannon Nelson  * @words: (in) number of words to read; (out) number of words actually read
3347073f46eSShannon Nelson  * @data: words read from the Shadow RAM
3357073f46eSShannon Nelson  *
3367073f46eSShannon Nelson  * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_aq()
3377073f46eSShannon Nelson  * method. The buffer read is preceded by the NVM ownership take
3387073f46eSShannon Nelson  * and followed by the release.
3397073f46eSShannon Nelson  **/
3407073f46eSShannon Nelson static i40e_status i40e_read_nvm_buffer_aq(struct i40e_hw *hw, u16 offset,
3417073f46eSShannon Nelson 					   u16 *words, u16 *data)
3427073f46eSShannon Nelson {
3437073f46eSShannon Nelson 	i40e_status ret_code;
3447073f46eSShannon Nelson 	u16 read_size = *words;
3457073f46eSShannon Nelson 	bool last_cmd = false;
3467073f46eSShannon Nelson 	u16 words_read = 0;
3477073f46eSShannon Nelson 	u16 i = 0;
3487073f46eSShannon Nelson 
3497073f46eSShannon Nelson 	do {
3507073f46eSShannon Nelson 		/* Calculate number of bytes we should read in this step.
3517073f46eSShannon Nelson 		 * FVL AQ do not allow to read more than one page at a time or
3527073f46eSShannon Nelson 		 * to cross page boundaries.
3537073f46eSShannon Nelson 		 */
3547073f46eSShannon Nelson 		if (offset % I40E_SR_SECTOR_SIZE_IN_WORDS)
3557073f46eSShannon Nelson 			read_size = min(*words,
3567073f46eSShannon Nelson 					(u16)(I40E_SR_SECTOR_SIZE_IN_WORDS -
3577073f46eSShannon Nelson 				      (offset % I40E_SR_SECTOR_SIZE_IN_WORDS)));
3587073f46eSShannon Nelson 		else
3597073f46eSShannon Nelson 			read_size = min((*words - words_read),
3607073f46eSShannon Nelson 					I40E_SR_SECTOR_SIZE_IN_WORDS);
3617073f46eSShannon Nelson 
3627073f46eSShannon Nelson 		/* Check if this is last command, if so set proper flag */
3637073f46eSShannon Nelson 		if ((words_read + read_size) >= *words)
3647073f46eSShannon Nelson 			last_cmd = true;
3657073f46eSShannon Nelson 
3667073f46eSShannon Nelson 		ret_code = i40e_read_nvm_aq(hw, 0x0, offset, read_size,
3677073f46eSShannon Nelson 					    data + words_read, last_cmd);
3687073f46eSShannon Nelson 		if (ret_code)
3697073f46eSShannon Nelson 			goto read_nvm_buffer_aq_exit;
3707073f46eSShannon Nelson 
3717073f46eSShannon Nelson 		/* Increment counter for words already read and move offset to
3727073f46eSShannon Nelson 		 * new read location
3737073f46eSShannon Nelson 		 */
3747073f46eSShannon Nelson 		words_read += read_size;
3757073f46eSShannon Nelson 		offset += read_size;
3767073f46eSShannon Nelson 	} while (words_read < *words);
3777073f46eSShannon Nelson 
3787073f46eSShannon Nelson 	for (i = 0; i < *words; i++)
3797073f46eSShannon Nelson 		data[i] = le16_to_cpu(((__le16 *)data)[i]);
3807073f46eSShannon Nelson 
3817073f46eSShannon Nelson read_nvm_buffer_aq_exit:
3827073f46eSShannon Nelson 	*words = words_read;
3837073f46eSShannon Nelson 	return ret_code;
3847073f46eSShannon Nelson }
3857073f46eSShannon Nelson 
3867073f46eSShannon Nelson /**
3873e26186dSShannon Nelson  * i40e_read_nvm_buffer - Reads Shadow RAM buffer
3883e26186dSShannon Nelson  * @hw: pointer to the HW structure
38956a62fc8SJesse Brandeburg  * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
3903e26186dSShannon Nelson  * @words: (in) number of words to read; (out) number of words actually read
3913e26186dSShannon Nelson  * @data: words read from the Shadow RAM
39256a62fc8SJesse Brandeburg  *
39356a62fc8SJesse Brandeburg  * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
39456a62fc8SJesse Brandeburg  * method. The buffer read is preceded by the NVM ownership take
39556a62fc8SJesse Brandeburg  * and followed by the release.
39656a62fc8SJesse Brandeburg  **/
39756a62fc8SJesse Brandeburg i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
39856a62fc8SJesse Brandeburg 				 u16 *words, u16 *data)
39956a62fc8SJesse Brandeburg {
4007073f46eSShannon Nelson 	if (hw->mac.type == I40E_MAC_X722)
4017073f46eSShannon Nelson 		return i40e_read_nvm_buffer_aq(hw, offset, words, data);
402d1bbe0eaSKamil Krawczyk 	return i40e_read_nvm_buffer_srctl(hw, offset, words, data);
40356a62fc8SJesse Brandeburg }
40456a62fc8SJesse Brandeburg 
40556a62fc8SJesse Brandeburg /**
406cd552cb4SShannon Nelson  * i40e_write_nvm_aq - Writes Shadow RAM.
407cd552cb4SShannon Nelson  * @hw: pointer to the HW structure.
408cd552cb4SShannon Nelson  * @module_pointer: module pointer location in words from the NVM beginning
409cd552cb4SShannon Nelson  * @offset: offset in words from module start
410cd552cb4SShannon Nelson  * @words: number of words to write
411cd552cb4SShannon Nelson  * @data: buffer with words to write to the Shadow RAM
412cd552cb4SShannon Nelson  * @last_command: tells the AdminQ that this is the last command
413cd552cb4SShannon Nelson  *
414cd552cb4SShannon Nelson  * Writes a 16 bit words buffer to the Shadow RAM using the admin command.
415cd552cb4SShannon Nelson  **/
416952d9639SWei Yongjun static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
417cd552cb4SShannon Nelson 				     u32 offset, u16 words, void *data,
418cd552cb4SShannon Nelson 				     bool last_command)
419cd552cb4SShannon Nelson {
420cd552cb4SShannon Nelson 	i40e_status ret_code = I40E_ERR_NVM;
4216b5c1b89SShannon Nelson 	struct i40e_asq_cmd_details cmd_details;
4226b5c1b89SShannon Nelson 
4236b5c1b89SShannon Nelson 	memset(&cmd_details, 0, sizeof(cmd_details));
4246b5c1b89SShannon Nelson 	cmd_details.wb_desc = &hw->nvm_wb_desc;
425cd552cb4SShannon Nelson 
426cd552cb4SShannon Nelson 	/* Here we are checking the SR limit only for the flat memory model.
427cd552cb4SShannon Nelson 	 * We cannot do it for the module-based model, as we did not acquire
428cd552cb4SShannon Nelson 	 * the NVM resource yet (we cannot get the module pointer value).
429cd552cb4SShannon Nelson 	 * Firmware will check the module-based model.
430cd552cb4SShannon Nelson 	 */
431cd552cb4SShannon Nelson 	if ((offset + words) > hw->nvm.sr_size)
43274d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
43374d0d0edSShannon Nelson 			   "NVM write error: offset %d beyond Shadow RAM limit %d\n",
43474d0d0edSShannon Nelson 			   (offset + words), hw->nvm.sr_size);
435cd552cb4SShannon Nelson 	else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS)
436cd552cb4SShannon Nelson 		/* We can write only up to 4KB (one sector), in one AQ write */
43774d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
43874d0d0edSShannon Nelson 			   "NVM write fail error: tried to write %d words, limit is %d.\n",
43974d0d0edSShannon Nelson 			   words, I40E_SR_SECTOR_SIZE_IN_WORDS);
440cd552cb4SShannon Nelson 	else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS)
441cd552cb4SShannon Nelson 		 != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS))
442cd552cb4SShannon Nelson 		/* A single write cannot spread over two sectors */
44374d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
44474d0d0edSShannon Nelson 			   "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n",
44574d0d0edSShannon Nelson 			   offset, words);
446cd552cb4SShannon Nelson 	else
447cd552cb4SShannon Nelson 		ret_code = i40e_aq_update_nvm(hw, module_pointer,
448cd552cb4SShannon Nelson 					      2 * offset,  /*bytes*/
449cd552cb4SShannon Nelson 					      2 * words,   /*bytes*/
4506b5c1b89SShannon Nelson 					      data, last_command, &cmd_details);
451cd552cb4SShannon Nelson 
452cd552cb4SShannon Nelson 	return ret_code;
453cd552cb4SShannon Nelson }
454cd552cb4SShannon Nelson 
455cd552cb4SShannon Nelson /**
45656a62fc8SJesse Brandeburg  * i40e_calc_nvm_checksum - Calculates and returns the checksum
45756a62fc8SJesse Brandeburg  * @hw: pointer to hardware structure
45898d44381SJeff Kirsher  * @checksum: pointer to the checksum
45956a62fc8SJesse Brandeburg  *
4603e26186dSShannon Nelson  * This function calculates SW Checksum that covers the whole 64kB shadow RAM
46156a62fc8SJesse Brandeburg  * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD
46256a62fc8SJesse Brandeburg  * is customer specific and unknown. Therefore, this function skips all maximum
46356a62fc8SJesse Brandeburg  * possible size of VPD (1kB).
46456a62fc8SJesse Brandeburg  **/
46556a62fc8SJesse Brandeburg static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
46656a62fc8SJesse Brandeburg 						    u16 *checksum)
46756a62fc8SJesse Brandeburg {
46856a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
469d1bbe0eaSKamil Krawczyk 	struct i40e_virt_mem vmem;
47056a62fc8SJesse Brandeburg 	u16 pcie_alt_module = 0;
47156a62fc8SJesse Brandeburg 	u16 checksum_local = 0;
47256a62fc8SJesse Brandeburg 	u16 vpd_module = 0;
473d1bbe0eaSKamil Krawczyk 	u16 *data;
474d1bbe0eaSKamil Krawczyk 	u16 i = 0;
475d1bbe0eaSKamil Krawczyk 
476d1bbe0eaSKamil Krawczyk 	ret_code = i40e_allocate_virt_mem(hw, &vmem,
477d1bbe0eaSKamil Krawczyk 				    I40E_SR_SECTOR_SIZE_IN_WORDS * sizeof(u16));
478d1bbe0eaSKamil Krawczyk 	if (ret_code)
479d1bbe0eaSKamil Krawczyk 		goto i40e_calc_nvm_checksum_exit;
480d1bbe0eaSKamil Krawczyk 	data = (u16 *)vmem.va;
48156a62fc8SJesse Brandeburg 
48256a62fc8SJesse Brandeburg 	/* read pointer to VPD area */
483a4bcfbb7SShannon Nelson 	ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module);
48456a62fc8SJesse Brandeburg 	if (ret_code) {
48556a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
48656a62fc8SJesse Brandeburg 		goto i40e_calc_nvm_checksum_exit;
48756a62fc8SJesse Brandeburg 	}
48856a62fc8SJesse Brandeburg 
48956a62fc8SJesse Brandeburg 	/* read pointer to PCIe Alt Auto-load module */
490a4bcfbb7SShannon Nelson 	ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR,
49156a62fc8SJesse Brandeburg 				      &pcie_alt_module);
49256a62fc8SJesse Brandeburg 	if (ret_code) {
49356a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
49456a62fc8SJesse Brandeburg 		goto i40e_calc_nvm_checksum_exit;
49556a62fc8SJesse Brandeburg 	}
49656a62fc8SJesse Brandeburg 
49756a62fc8SJesse Brandeburg 	/* Calculate SW checksum that covers the whole 64kB shadow RAM
49856a62fc8SJesse Brandeburg 	 * except the VPD and PCIe ALT Auto-load modules
49956a62fc8SJesse Brandeburg 	 */
50056a62fc8SJesse Brandeburg 	for (i = 0; i < hw->nvm.sr_size; i++) {
501d1bbe0eaSKamil Krawczyk 		/* Read SR page */
502d1bbe0eaSKamil Krawczyk 		if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) {
503d1bbe0eaSKamil Krawczyk 			u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS;
50456a62fc8SJesse Brandeburg 
505d1bbe0eaSKamil Krawczyk 			ret_code = i40e_read_nvm_buffer(hw, i, &words, data);
50656a62fc8SJesse Brandeburg 			if (ret_code) {
50756a62fc8SJesse Brandeburg 				ret_code = I40E_ERR_NVM_CHECKSUM;
50856a62fc8SJesse Brandeburg 				goto i40e_calc_nvm_checksum_exit;
50956a62fc8SJesse Brandeburg 			}
510d1bbe0eaSKamil Krawczyk 		}
511d1bbe0eaSKamil Krawczyk 
512d1bbe0eaSKamil Krawczyk 		/* Skip Checksum word */
513d1bbe0eaSKamil Krawczyk 		if (i == I40E_SR_SW_CHECKSUM_WORD)
514d1bbe0eaSKamil Krawczyk 			continue;
515d1bbe0eaSKamil Krawczyk 		/* Skip VPD module (convert byte size to word count) */
516d1bbe0eaSKamil Krawczyk 		if ((i >= (u32)vpd_module) &&
517d1bbe0eaSKamil Krawczyk 		    (i < ((u32)vpd_module +
518d1bbe0eaSKamil Krawczyk 		     (I40E_SR_VPD_MODULE_MAX_SIZE / 2)))) {
519d1bbe0eaSKamil Krawczyk 			continue;
520d1bbe0eaSKamil Krawczyk 		}
521d1bbe0eaSKamil Krawczyk 		/* Skip PCIe ALT module (convert byte size to word count) */
522d1bbe0eaSKamil Krawczyk 		if ((i >= (u32)pcie_alt_module) &&
523d1bbe0eaSKamil Krawczyk 		    (i < ((u32)pcie_alt_module +
524d1bbe0eaSKamil Krawczyk 		     (I40E_SR_PCIE_ALT_MODULE_MAX_SIZE / 2)))) {
525d1bbe0eaSKamil Krawczyk 			continue;
526d1bbe0eaSKamil Krawczyk 		}
527d1bbe0eaSKamil Krawczyk 
528d1bbe0eaSKamil Krawczyk 		checksum_local += data[i % I40E_SR_SECTOR_SIZE_IN_WORDS];
52956a62fc8SJesse Brandeburg 	}
53056a62fc8SJesse Brandeburg 
53156a62fc8SJesse Brandeburg 	*checksum = (u16)I40E_SR_SW_CHECKSUM_BASE - checksum_local;
53256a62fc8SJesse Brandeburg 
53356a62fc8SJesse Brandeburg i40e_calc_nvm_checksum_exit:
534d1bbe0eaSKamil Krawczyk 	i40e_free_virt_mem(hw, &vmem);
53556a62fc8SJesse Brandeburg 	return ret_code;
53656a62fc8SJesse Brandeburg }
53756a62fc8SJesse Brandeburg 
53856a62fc8SJesse Brandeburg /**
539cd552cb4SShannon Nelson  * i40e_update_nvm_checksum - Updates the NVM checksum
540cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
541cd552cb4SShannon Nelson  *
542cd552cb4SShannon Nelson  * NVM ownership must be acquired before calling this function and released
543cd552cb4SShannon Nelson  * on ARQ completion event reception by caller.
544cd552cb4SShannon Nelson  * This function will commit SR to NVM.
545cd552cb4SShannon Nelson  **/
546cd552cb4SShannon Nelson i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw)
547cd552cb4SShannon Nelson {
548cd552cb4SShannon Nelson 	i40e_status ret_code = 0;
549cd552cb4SShannon Nelson 	u16 checksum;
550dd38c583SJesse Brandeburg 	__le16 le_sum;
551cd552cb4SShannon Nelson 
552cd552cb4SShannon Nelson 	ret_code = i40e_calc_nvm_checksum(hw, &checksum);
553dd38c583SJesse Brandeburg 	le_sum = cpu_to_le16(checksum);
554cd552cb4SShannon Nelson 	if (!ret_code)
555cd552cb4SShannon Nelson 		ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD,
556dd38c583SJesse Brandeburg 					     1, &le_sum, true);
557cd552cb4SShannon Nelson 
558cd552cb4SShannon Nelson 	return ret_code;
559cd552cb4SShannon Nelson }
560cd552cb4SShannon Nelson 
561cd552cb4SShannon Nelson /**
56256a62fc8SJesse Brandeburg  * i40e_validate_nvm_checksum - Validate EEPROM checksum
56356a62fc8SJesse Brandeburg  * @hw: pointer to hardware structure
56456a62fc8SJesse Brandeburg  * @checksum: calculated checksum
56556a62fc8SJesse Brandeburg  *
56656a62fc8SJesse Brandeburg  * Performs checksum calculation and validates the NVM SW checksum. If the
56756a62fc8SJesse Brandeburg  * caller does not need checksum, the value can be NULL.
56856a62fc8SJesse Brandeburg  **/
56956a62fc8SJesse Brandeburg i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
57056a62fc8SJesse Brandeburg 						 u16 *checksum)
57156a62fc8SJesse Brandeburg {
57256a62fc8SJesse Brandeburg 	i40e_status ret_code = 0;
57356a62fc8SJesse Brandeburg 	u16 checksum_sr = 0;
574e15c9fa0SJesse Brandeburg 	u16 checksum_local = 0;
57556a62fc8SJesse Brandeburg 
57656a62fc8SJesse Brandeburg 	ret_code = i40e_calc_nvm_checksum(hw, &checksum_local);
57756a62fc8SJesse Brandeburg 	if (ret_code)
5787a208e83SKamil Krawczyk 		goto i40e_validate_nvm_checksum_exit;
57956a62fc8SJesse Brandeburg 
58056a62fc8SJesse Brandeburg 	/* Do not use i40e_read_nvm_word() because we do not want to take
58156a62fc8SJesse Brandeburg 	 * the synchronization semaphores twice here.
58256a62fc8SJesse Brandeburg 	 */
583a4bcfbb7SShannon Nelson 	i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr);
58456a62fc8SJesse Brandeburg 
58556a62fc8SJesse Brandeburg 	/* Verify read checksum from EEPROM is the same as
58656a62fc8SJesse Brandeburg 	 * calculated checksum
58756a62fc8SJesse Brandeburg 	 */
58856a62fc8SJesse Brandeburg 	if (checksum_local != checksum_sr)
58956a62fc8SJesse Brandeburg 		ret_code = I40E_ERR_NVM_CHECKSUM;
59056a62fc8SJesse Brandeburg 
59156a62fc8SJesse Brandeburg 	/* If the user cares, return the calculated checksum */
59256a62fc8SJesse Brandeburg 	if (checksum)
59356a62fc8SJesse Brandeburg 		*checksum = checksum_local;
59456a62fc8SJesse Brandeburg 
59556a62fc8SJesse Brandeburg i40e_validate_nvm_checksum_exit:
59656a62fc8SJesse Brandeburg 	return ret_code;
59756a62fc8SJesse Brandeburg }
598cd552cb4SShannon Nelson 
599cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
600cd552cb4SShannon Nelson 					  struct i40e_nvm_access *cmd,
60179afe839SShannon Nelson 					  u8 *bytes, int *perrno);
602cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
603cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
60479afe839SShannon Nelson 					     u8 *bytes, int *perrno);
605cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
606cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
607cd552cb4SShannon Nelson 					     u8 *bytes, int *errno);
608cd552cb4SShannon Nelson static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
609cd552cb4SShannon Nelson 						struct i40e_nvm_access *cmd,
61079afe839SShannon Nelson 						int *perrno);
611cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
612cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
61379afe839SShannon Nelson 					 int *perrno);
614cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
615cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
61679afe839SShannon Nelson 					 u8 *bytes, int *perrno);
617cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
618cd552cb4SShannon Nelson 					struct i40e_nvm_access *cmd,
61979afe839SShannon Nelson 					u8 *bytes, int *perrno);
620e4c83c20SShannon Nelson static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw,
621e4c83c20SShannon Nelson 				       struct i40e_nvm_access *cmd,
622e4c83c20SShannon Nelson 				       u8 *bytes, int *perrno);
623b72dc7b1SShannon Nelson static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw,
624b72dc7b1SShannon Nelson 					     struct i40e_nvm_access *cmd,
625b72dc7b1SShannon Nelson 					     u8 *bytes, int *perrno);
626cd552cb4SShannon Nelson static inline u8 i40e_nvmupd_get_module(u32 val)
627cd552cb4SShannon Nelson {
628cd552cb4SShannon Nelson 	return (u8)(val & I40E_NVM_MOD_PNT_MASK);
629cd552cb4SShannon Nelson }
630cd552cb4SShannon Nelson static inline u8 i40e_nvmupd_get_transaction(u32 val)
631cd552cb4SShannon Nelson {
632cd552cb4SShannon Nelson 	return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT);
633cd552cb4SShannon Nelson }
634cd552cb4SShannon Nelson 
63574d0d0edSShannon Nelson static char *i40e_nvm_update_state_str[] = {
63674d0d0edSShannon Nelson 	"I40E_NVMUPD_INVALID",
63774d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_CON",
63874d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_SNT",
63974d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_LCB",
64074d0d0edSShannon Nelson 	"I40E_NVMUPD_READ_SA",
64174d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_ERA",
64274d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_CON",
64374d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_SNT",
64474d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_LCB",
64574d0d0edSShannon Nelson 	"I40E_NVMUPD_WRITE_SA",
64674d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_CON",
64774d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_SA",
64874d0d0edSShannon Nelson 	"I40E_NVMUPD_CSUM_LCB",
6490af8e9dbSShannon Nelson 	"I40E_NVMUPD_STATUS",
650e4c83c20SShannon Nelson 	"I40E_NVMUPD_EXEC_AQ",
651b72dc7b1SShannon Nelson 	"I40E_NVMUPD_GET_AQ_RESULT",
65274d0d0edSShannon Nelson };
65374d0d0edSShannon Nelson 
654cd552cb4SShannon Nelson /**
655cd552cb4SShannon Nelson  * i40e_nvmupd_command - Process an NVM update command
656cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
657cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command
658cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
65979afe839SShannon Nelson  * @perrno: pointer to return error code
660cd552cb4SShannon Nelson  *
661cd552cb4SShannon Nelson  * Dispatches command depending on what update state is current
662cd552cb4SShannon Nelson  **/
663cd552cb4SShannon Nelson i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
664cd552cb4SShannon Nelson 				struct i40e_nvm_access *cmd,
66579afe839SShannon Nelson 				u8 *bytes, int *perrno)
666cd552cb4SShannon Nelson {
667cd552cb4SShannon Nelson 	i40e_status status;
6680af8e9dbSShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
669cd552cb4SShannon Nelson 
670cd552cb4SShannon Nelson 	/* assume success */
67179afe839SShannon Nelson 	*perrno = 0;
672cd552cb4SShannon Nelson 
6730af8e9dbSShannon Nelson 	/* early check for status command and debug msgs */
6740af8e9dbSShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
6750af8e9dbSShannon Nelson 
6760af8e9dbSShannon Nelson 	i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n",
6770af8e9dbSShannon Nelson 		   i40e_nvm_update_state_str[upd_cmd],
6780af8e9dbSShannon Nelson 		   hw->nvmupd_state,
6790af8e9dbSShannon Nelson 		   hw->aq.nvm_release_on_done);
6800af8e9dbSShannon Nelson 
6810af8e9dbSShannon Nelson 	if (upd_cmd == I40E_NVMUPD_INVALID) {
6820af8e9dbSShannon Nelson 		*perrno = -EFAULT;
6830af8e9dbSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
6840af8e9dbSShannon Nelson 			   "i40e_nvmupd_validate_command returns %d errno %d\n",
6850af8e9dbSShannon Nelson 			   upd_cmd, *perrno);
6860af8e9dbSShannon Nelson 	}
6870af8e9dbSShannon Nelson 
6880af8e9dbSShannon Nelson 	/* a status request returns immediately rather than
6890af8e9dbSShannon Nelson 	 * going into the state machine
6900af8e9dbSShannon Nelson 	 */
6910af8e9dbSShannon Nelson 	if (upd_cmd == I40E_NVMUPD_STATUS) {
6920af8e9dbSShannon Nelson 		bytes[0] = hw->nvmupd_state;
6930af8e9dbSShannon Nelson 		return 0;
6940af8e9dbSShannon Nelson 	}
6950af8e9dbSShannon Nelson 
696cd552cb4SShannon Nelson 	switch (hw->nvmupd_state) {
697cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_INIT:
69879afe839SShannon Nelson 		status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno);
699cd552cb4SShannon Nelson 		break;
700cd552cb4SShannon Nelson 
701cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_READING:
70279afe839SShannon Nelson 		status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno);
703cd552cb4SShannon Nelson 		break;
704cd552cb4SShannon Nelson 
705cd552cb4SShannon Nelson 	case I40E_NVMUPD_STATE_WRITING:
70679afe839SShannon Nelson 		status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno);
707cd552cb4SShannon Nelson 		break;
708cd552cb4SShannon Nelson 
7092f1b5bc8SShannon Nelson 	case I40E_NVMUPD_STATE_INIT_WAIT:
7102f1b5bc8SShannon Nelson 	case I40E_NVMUPD_STATE_WRITE_WAIT:
7112f1b5bc8SShannon Nelson 		status = I40E_ERR_NOT_READY;
7122f1b5bc8SShannon Nelson 		*perrno = -EBUSY;
7132f1b5bc8SShannon Nelson 		break;
7142f1b5bc8SShannon Nelson 
715cd552cb4SShannon Nelson 	default:
716cd552cb4SShannon Nelson 		/* invalid state, should never happen */
71774d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
71874d0d0edSShannon Nelson 			   "NVMUPD: no such state %d\n", hw->nvmupd_state);
719cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
72079afe839SShannon Nelson 		*perrno = -ESRCH;
721cd552cb4SShannon Nelson 		break;
722cd552cb4SShannon Nelson 	}
723cd552cb4SShannon Nelson 	return status;
724cd552cb4SShannon Nelson }
725cd552cb4SShannon Nelson 
726cd552cb4SShannon Nelson /**
727cd552cb4SShannon Nelson  * i40e_nvmupd_state_init - Handle NVM update state Init
728cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
729cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
730cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
73179afe839SShannon Nelson  * @perrno: pointer to return error code
732cd552cb4SShannon Nelson  *
733cd552cb4SShannon Nelson  * Process legitimate commands of the Init state and conditionally set next
734cd552cb4SShannon Nelson  * state. Reject all other commands.
735cd552cb4SShannon Nelson  **/
736cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
737cd552cb4SShannon Nelson 					  struct i40e_nvm_access *cmd,
73879afe839SShannon Nelson 					  u8 *bytes, int *perrno)
739cd552cb4SShannon Nelson {
740cd552cb4SShannon Nelson 	i40e_status status = 0;
741cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
742cd552cb4SShannon Nelson 
74379afe839SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
744cd552cb4SShannon Nelson 
745cd552cb4SShannon Nelson 	switch (upd_cmd) {
746cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SA:
747cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
748cd552cb4SShannon Nelson 		if (status) {
74979afe839SShannon Nelson 			*perrno = i40e_aq_rc_to_posix(status,
750bf848f32SShannon Nelson 						     hw->aq.asq_last_status);
751cd552cb4SShannon Nelson 		} else {
75279afe839SShannon Nelson 			status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
753cd552cb4SShannon Nelson 			i40e_release_nvm(hw);
754cd552cb4SShannon Nelson 		}
755cd552cb4SShannon Nelson 		break;
756cd552cb4SShannon Nelson 
757cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SNT:
758cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
759cd552cb4SShannon Nelson 		if (status) {
76079afe839SShannon Nelson 			*perrno = i40e_aq_rc_to_posix(status,
761bf848f32SShannon Nelson 						     hw->aq.asq_last_status);
762cd552cb4SShannon Nelson 		} else {
76379afe839SShannon Nelson 			status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
7640fdd052cSShannon Nelson 			if (status)
7650fdd052cSShannon Nelson 				i40e_release_nvm(hw);
7660fdd052cSShannon Nelson 			else
767cd552cb4SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_READING;
768cd552cb4SShannon Nelson 		}
769cd552cb4SShannon Nelson 		break;
770cd552cb4SShannon Nelson 
771cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_ERA:
772cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
773cd552cb4SShannon Nelson 		if (status) {
77479afe839SShannon Nelson 			*perrno = i40e_aq_rc_to_posix(status,
775bf848f32SShannon Nelson 						     hw->aq.asq_last_status);
776cd552cb4SShannon Nelson 		} else {
77779afe839SShannon Nelson 			status = i40e_nvmupd_nvm_erase(hw, cmd, perrno);
7782f1b5bc8SShannon Nelson 			if (status) {
779cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
7802f1b5bc8SShannon Nelson 			} else {
781cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
7822f1b5bc8SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
7832f1b5bc8SShannon Nelson 			}
784cd552cb4SShannon Nelson 		}
785cd552cb4SShannon Nelson 		break;
786cd552cb4SShannon Nelson 
787cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_SA:
788cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
789cd552cb4SShannon Nelson 		if (status) {
79079afe839SShannon Nelson 			*perrno = i40e_aq_rc_to_posix(status,
791bf848f32SShannon Nelson 						     hw->aq.asq_last_status);
792cd552cb4SShannon Nelson 		} else {
79379afe839SShannon Nelson 			status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
7942f1b5bc8SShannon Nelson 			if (status) {
795cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
7962f1b5bc8SShannon Nelson 			} else {
797cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
7982f1b5bc8SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
7992f1b5bc8SShannon Nelson 			}
800cd552cb4SShannon Nelson 		}
801cd552cb4SShannon Nelson 		break;
802cd552cb4SShannon Nelson 
803cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_SNT:
804cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
805cd552cb4SShannon Nelson 		if (status) {
80679afe839SShannon Nelson 			*perrno = i40e_aq_rc_to_posix(status,
807bf848f32SShannon Nelson 						     hw->aq.asq_last_status);
808cd552cb4SShannon Nelson 		} else {
80979afe839SShannon Nelson 			status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
8100fdd052cSShannon Nelson 			if (status)
8110fdd052cSShannon Nelson 				i40e_release_nvm(hw);
8120fdd052cSShannon Nelson 			else
8132f1b5bc8SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
814cd552cb4SShannon Nelson 		}
815cd552cb4SShannon Nelson 		break;
816cd552cb4SShannon Nelson 
817cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_SA:
818cd552cb4SShannon Nelson 		status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
819cd552cb4SShannon Nelson 		if (status) {
82079afe839SShannon Nelson 			*perrno = i40e_aq_rc_to_posix(status,
821bf848f32SShannon Nelson 						     hw->aq.asq_last_status);
822cd552cb4SShannon Nelson 		} else {
823cd552cb4SShannon Nelson 			status = i40e_update_nvm_checksum(hw);
824cd552cb4SShannon Nelson 			if (status) {
82579afe839SShannon Nelson 				*perrno = hw->aq.asq_last_status ?
826bf848f32SShannon Nelson 				   i40e_aq_rc_to_posix(status,
827bf848f32SShannon Nelson 						       hw->aq.asq_last_status) :
828cd552cb4SShannon Nelson 				   -EIO;
829cd552cb4SShannon Nelson 				i40e_release_nvm(hw);
830cd552cb4SShannon Nelson 			} else {
831cd552cb4SShannon Nelson 				hw->aq.nvm_release_on_done = true;
8322f1b5bc8SShannon Nelson 				hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
833cd552cb4SShannon Nelson 			}
834cd552cb4SShannon Nelson 		}
835cd552cb4SShannon Nelson 		break;
836cd552cb4SShannon Nelson 
837e4c83c20SShannon Nelson 	case I40E_NVMUPD_EXEC_AQ:
838e4c83c20SShannon Nelson 		status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno);
839e4c83c20SShannon Nelson 		break;
840e4c83c20SShannon Nelson 
841b72dc7b1SShannon Nelson 	case I40E_NVMUPD_GET_AQ_RESULT:
842b72dc7b1SShannon Nelson 		status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno);
843b72dc7b1SShannon Nelson 		break;
844b72dc7b1SShannon Nelson 
845cd552cb4SShannon Nelson 	default:
84674d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
84774d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in init state\n",
84874d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
849cd552cb4SShannon Nelson 		status = I40E_ERR_NVM;
85079afe839SShannon Nelson 		*perrno = -ESRCH;
851cd552cb4SShannon Nelson 		break;
852cd552cb4SShannon Nelson 	}
853cd552cb4SShannon Nelson 	return status;
854cd552cb4SShannon Nelson }
855cd552cb4SShannon Nelson 
856cd552cb4SShannon Nelson /**
857cd552cb4SShannon Nelson  * i40e_nvmupd_state_reading - Handle NVM update state Reading
858cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
859cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
860cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
86179afe839SShannon Nelson  * @perrno: pointer to return error code
862cd552cb4SShannon Nelson  *
863cd552cb4SShannon Nelson  * NVM ownership is already held.  Process legitimate commands and set any
864cd552cb4SShannon Nelson  * change in state; reject all other commands.
865cd552cb4SShannon Nelson  **/
866cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
867cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
86879afe839SShannon Nelson 					     u8 *bytes, int *perrno)
869cd552cb4SShannon Nelson {
8702f1b5bc8SShannon Nelson 	i40e_status status = 0;
871cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
872cd552cb4SShannon Nelson 
87379afe839SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
874cd552cb4SShannon Nelson 
875cd552cb4SShannon Nelson 	switch (upd_cmd) {
876cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_SA:
877cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_CON:
87879afe839SShannon Nelson 		status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
879cd552cb4SShannon Nelson 		break;
880cd552cb4SShannon Nelson 
881cd552cb4SShannon Nelson 	case I40E_NVMUPD_READ_LCB:
88279afe839SShannon Nelson 		status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
883cd552cb4SShannon Nelson 		i40e_release_nvm(hw);
884cd552cb4SShannon Nelson 		hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
885cd552cb4SShannon Nelson 		break;
886cd552cb4SShannon Nelson 
887cd552cb4SShannon Nelson 	default:
88874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
88974d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in reading state.\n",
89074d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
891cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
89279afe839SShannon Nelson 		*perrno = -ESRCH;
893cd552cb4SShannon Nelson 		break;
894cd552cb4SShannon Nelson 	}
895cd552cb4SShannon Nelson 	return status;
896cd552cb4SShannon Nelson }
897cd552cb4SShannon Nelson 
898cd552cb4SShannon Nelson /**
899cd552cb4SShannon Nelson  * i40e_nvmupd_state_writing - Handle NVM update state Writing
900cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
901cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
902cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
90379afe839SShannon Nelson  * @perrno: pointer to return error code
904cd552cb4SShannon Nelson  *
905cd552cb4SShannon Nelson  * NVM ownership is already held.  Process legitimate commands and set any
906cd552cb4SShannon Nelson  * change in state; reject all other commands
907cd552cb4SShannon Nelson  **/
908cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
909cd552cb4SShannon Nelson 					     struct i40e_nvm_access *cmd,
91079afe839SShannon Nelson 					     u8 *bytes, int *perrno)
911cd552cb4SShannon Nelson {
9122f1b5bc8SShannon Nelson 	i40e_status status = 0;
913cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
9142c47e351SShannon Nelson 	bool retry_attempt = false;
915cd552cb4SShannon Nelson 
91679afe839SShannon Nelson 	upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
917cd552cb4SShannon Nelson 
9182c47e351SShannon Nelson retry:
919cd552cb4SShannon Nelson 	switch (upd_cmd) {
920cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_CON:
92179afe839SShannon Nelson 		status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
9222f1b5bc8SShannon Nelson 		if (!status)
9232f1b5bc8SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
924cd552cb4SShannon Nelson 		break;
925cd552cb4SShannon Nelson 
926cd552cb4SShannon Nelson 	case I40E_NVMUPD_WRITE_LCB:
92779afe839SShannon Nelson 		status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
9282f1b5bc8SShannon Nelson 		if (status) {
9292f1b5bc8SShannon Nelson 			*perrno = hw->aq.asq_last_status ?
9302f1b5bc8SShannon Nelson 				   i40e_aq_rc_to_posix(status,
9312f1b5bc8SShannon Nelson 						       hw->aq.asq_last_status) :
9322f1b5bc8SShannon Nelson 				   -EIO;
933cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
9342f1b5bc8SShannon Nelson 		} else {
9352f1b5bc8SShannon Nelson 			hw->aq.nvm_release_on_done = true;
9362f1b5bc8SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
9372f1b5bc8SShannon Nelson 		}
938cd552cb4SShannon Nelson 		break;
939cd552cb4SShannon Nelson 
940cd552cb4SShannon Nelson 	case I40E_NVMUPD_CSUM_CON:
941cd552cb4SShannon Nelson 		status = i40e_update_nvm_checksum(hw);
942cd552cb4SShannon Nelson 		if (status) {
94379afe839SShannon Nelson 			*perrno = hw->aq.asq_last_status ?
944bf848f32SShannon Nelson 				   i40e_aq_rc_to_posix(status,
945bf848f32SShannon Nelson 						       hw->aq.asq_last_status) :
946cd552cb4SShannon Nelson 				   -EIO;
947cd552cb4SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
9482f1b5bc8SShannon Nelson 		} else {
9492f1b5bc8SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
950cd552cb4SShannon Nelson 		}
951cd552cb4SShannon Nelson 		break;
952cd552cb4SShannon Nelson 
9530fdd052cSShannon Nelson 	case I40E_NVMUPD_CSUM_LCB:
9540fdd052cSShannon Nelson 		status = i40e_update_nvm_checksum(hw);
9552f1b5bc8SShannon Nelson 		if (status) {
95679afe839SShannon Nelson 			*perrno = hw->aq.asq_last_status ?
957bf848f32SShannon Nelson 				   i40e_aq_rc_to_posix(status,
958bf848f32SShannon Nelson 						       hw->aq.asq_last_status) :
9590fdd052cSShannon Nelson 				   -EIO;
9600fdd052cSShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
9612f1b5bc8SShannon Nelson 		} else {
9622f1b5bc8SShannon Nelson 			hw->aq.nvm_release_on_done = true;
9632f1b5bc8SShannon Nelson 			hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
9642f1b5bc8SShannon Nelson 		}
9650fdd052cSShannon Nelson 		break;
9660fdd052cSShannon Nelson 
967cd552cb4SShannon Nelson 	default:
96874d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
96974d0d0edSShannon Nelson 			   "NVMUPD: bad cmd %s in writing state.\n",
97074d0d0edSShannon Nelson 			   i40e_nvm_update_state_str[upd_cmd]);
971cd552cb4SShannon Nelson 		status = I40E_NOT_SUPPORTED;
97279afe839SShannon Nelson 		*perrno = -ESRCH;
973cd552cb4SShannon Nelson 		break;
974cd552cb4SShannon Nelson 	}
9752c47e351SShannon Nelson 
9762c47e351SShannon Nelson 	/* In some circumstances, a multi-write transaction takes longer
9772c47e351SShannon Nelson 	 * than the default 3 minute timeout on the write semaphore.  If
9782c47e351SShannon Nelson 	 * the write failed with an EBUSY status, this is likely the problem,
9792c47e351SShannon Nelson 	 * so here we try to reacquire the semaphore then retry the write.
9802c47e351SShannon Nelson 	 * We only do one retry, then give up.
9812c47e351SShannon Nelson 	 */
9822c47e351SShannon Nelson 	if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) &&
9832c47e351SShannon Nelson 	    !retry_attempt) {
9842c47e351SShannon Nelson 		i40e_status old_status = status;
9852c47e351SShannon Nelson 		u32 old_asq_status = hw->aq.asq_last_status;
9862c47e351SShannon Nelson 		u32 gtime;
9872c47e351SShannon Nelson 
9882c47e351SShannon Nelson 		gtime = rd32(hw, I40E_GLVFGEN_TIMER);
9892c47e351SShannon Nelson 		if (gtime >= hw->nvm.hw_semaphore_timeout) {
9902c47e351SShannon Nelson 			i40e_debug(hw, I40E_DEBUG_ALL,
9912c47e351SShannon Nelson 				   "NVMUPD: write semaphore expired (%d >= %lld), retrying\n",
9922c47e351SShannon Nelson 				   gtime, hw->nvm.hw_semaphore_timeout);
9932c47e351SShannon Nelson 			i40e_release_nvm(hw);
9942c47e351SShannon Nelson 			status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
9952c47e351SShannon Nelson 			if (status) {
9962c47e351SShannon Nelson 				i40e_debug(hw, I40E_DEBUG_ALL,
9972c47e351SShannon Nelson 					   "NVMUPD: write semaphore reacquire failed aq_err = %d\n",
9982c47e351SShannon Nelson 					   hw->aq.asq_last_status);
9992c47e351SShannon Nelson 				status = old_status;
10002c47e351SShannon Nelson 				hw->aq.asq_last_status = old_asq_status;
10012c47e351SShannon Nelson 			} else {
10022c47e351SShannon Nelson 				retry_attempt = true;
10032c47e351SShannon Nelson 				goto retry;
10042c47e351SShannon Nelson 			}
10052c47e351SShannon Nelson 		}
10062c47e351SShannon Nelson 	}
10072c47e351SShannon Nelson 
1008cd552cb4SShannon Nelson 	return status;
1009cd552cb4SShannon Nelson }
1010cd552cb4SShannon Nelson 
1011cd552cb4SShannon Nelson /**
1012cd552cb4SShannon Nelson  * i40e_nvmupd_validate_command - Validate given command
1013cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
1014cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
101579afe839SShannon Nelson  * @perrno: pointer to return error code
1016cd552cb4SShannon Nelson  *
1017cd552cb4SShannon Nelson  * Return one of the valid command types or I40E_NVMUPD_INVALID
1018cd552cb4SShannon Nelson  **/
1019cd552cb4SShannon Nelson static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
1020cd552cb4SShannon Nelson 						 struct i40e_nvm_access *cmd,
102179afe839SShannon Nelson 						 int *perrno)
1022cd552cb4SShannon Nelson {
1023cd552cb4SShannon Nelson 	enum i40e_nvmupd_cmd upd_cmd;
10240af8e9dbSShannon Nelson 	u8 module, transaction;
1025cd552cb4SShannon Nelson 
1026cd552cb4SShannon Nelson 	/* anything that doesn't match a recognized case is an error */
1027cd552cb4SShannon Nelson 	upd_cmd = I40E_NVMUPD_INVALID;
1028cd552cb4SShannon Nelson 
1029cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
10300af8e9dbSShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
1031cd552cb4SShannon Nelson 
1032cd552cb4SShannon Nelson 	/* limits on data size */
1033cd552cb4SShannon Nelson 	if ((cmd->data_size < 1) ||
1034cd552cb4SShannon Nelson 	    (cmd->data_size > I40E_NVMUPD_MAX_DATA)) {
103574d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
103674d0d0edSShannon Nelson 			   "i40e_nvmupd_validate_command data_size %d\n",
1037cd552cb4SShannon Nelson 			   cmd->data_size);
103879afe839SShannon Nelson 		*perrno = -EFAULT;
1039cd552cb4SShannon Nelson 		return I40E_NVMUPD_INVALID;
1040cd552cb4SShannon Nelson 	}
1041cd552cb4SShannon Nelson 
1042cd552cb4SShannon Nelson 	switch (cmd->command) {
1043cd552cb4SShannon Nelson 	case I40E_NVM_READ:
1044cd552cb4SShannon Nelson 		switch (transaction) {
1045cd552cb4SShannon Nelson 		case I40E_NVM_CON:
1046cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_CON;
1047cd552cb4SShannon Nelson 			break;
1048cd552cb4SShannon Nelson 		case I40E_NVM_SNT:
1049cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_SNT;
1050cd552cb4SShannon Nelson 			break;
1051cd552cb4SShannon Nelson 		case I40E_NVM_LCB:
1052cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_LCB;
1053cd552cb4SShannon Nelson 			break;
1054cd552cb4SShannon Nelson 		case I40E_NVM_SA:
1055cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_READ_SA;
1056cd552cb4SShannon Nelson 			break;
10570af8e9dbSShannon Nelson 		case I40E_NVM_EXEC:
10580af8e9dbSShannon Nelson 			if (module == 0xf)
10590af8e9dbSShannon Nelson 				upd_cmd = I40E_NVMUPD_STATUS;
1060b72dc7b1SShannon Nelson 			else if (module == 0)
1061b72dc7b1SShannon Nelson 				upd_cmd = I40E_NVMUPD_GET_AQ_RESULT;
10620af8e9dbSShannon Nelson 			break;
1063cd552cb4SShannon Nelson 		}
1064cd552cb4SShannon Nelson 		break;
1065cd552cb4SShannon Nelson 
1066cd552cb4SShannon Nelson 	case I40E_NVM_WRITE:
1067cd552cb4SShannon Nelson 		switch (transaction) {
1068cd552cb4SShannon Nelson 		case I40E_NVM_CON:
1069cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_CON;
1070cd552cb4SShannon Nelson 			break;
1071cd552cb4SShannon Nelson 		case I40E_NVM_SNT:
1072cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_SNT;
1073cd552cb4SShannon Nelson 			break;
1074cd552cb4SShannon Nelson 		case I40E_NVM_LCB:
1075cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_LCB;
1076cd552cb4SShannon Nelson 			break;
1077cd552cb4SShannon Nelson 		case I40E_NVM_SA:
1078cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_SA;
1079cd552cb4SShannon Nelson 			break;
1080cd552cb4SShannon Nelson 		case I40E_NVM_ERA:
1081cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_WRITE_ERA;
1082cd552cb4SShannon Nelson 			break;
1083cd552cb4SShannon Nelson 		case I40E_NVM_CSUM:
1084cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_CON;
1085cd552cb4SShannon Nelson 			break;
1086cd552cb4SShannon Nelson 		case (I40E_NVM_CSUM|I40E_NVM_SA):
1087cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_SA;
1088cd552cb4SShannon Nelson 			break;
1089cd552cb4SShannon Nelson 		case (I40E_NVM_CSUM|I40E_NVM_LCB):
1090cd552cb4SShannon Nelson 			upd_cmd = I40E_NVMUPD_CSUM_LCB;
1091cd552cb4SShannon Nelson 			break;
1092e4c83c20SShannon Nelson 		case I40E_NVM_EXEC:
1093e4c83c20SShannon Nelson 			if (module == 0)
1094e4c83c20SShannon Nelson 				upd_cmd = I40E_NVMUPD_EXEC_AQ;
1095e4c83c20SShannon Nelson 			break;
1096cd552cb4SShannon Nelson 		}
1097cd552cb4SShannon Nelson 		break;
1098cd552cb4SShannon Nelson 	}
1099cd552cb4SShannon Nelson 
1100cd552cb4SShannon Nelson 	return upd_cmd;
1101cd552cb4SShannon Nelson }
1102cd552cb4SShannon Nelson 
1103cd552cb4SShannon Nelson /**
1104e4c83c20SShannon Nelson  * i40e_nvmupd_exec_aq - Run an AQ command
1105e4c83c20SShannon Nelson  * @hw: pointer to hardware structure
1106e4c83c20SShannon Nelson  * @cmd: pointer to nvm update command buffer
1107e4c83c20SShannon Nelson  * @bytes: pointer to the data buffer
1108e4c83c20SShannon Nelson  * @perrno: pointer to return error code
1109e4c83c20SShannon Nelson  *
1110e4c83c20SShannon Nelson  * cmd structure contains identifiers and data buffer
1111e4c83c20SShannon Nelson  **/
1112e4c83c20SShannon Nelson static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw,
1113e4c83c20SShannon Nelson 				       struct i40e_nvm_access *cmd,
1114e4c83c20SShannon Nelson 				       u8 *bytes, int *perrno)
1115e4c83c20SShannon Nelson {
1116e4c83c20SShannon Nelson 	struct i40e_asq_cmd_details cmd_details;
1117e4c83c20SShannon Nelson 	i40e_status status;
1118e4c83c20SShannon Nelson 	struct i40e_aq_desc *aq_desc;
1119e4c83c20SShannon Nelson 	u32 buff_size = 0;
1120e4c83c20SShannon Nelson 	u8 *buff = NULL;
1121e4c83c20SShannon Nelson 	u32 aq_desc_len;
1122e4c83c20SShannon Nelson 	u32 aq_data_len;
1123e4c83c20SShannon Nelson 
1124e4c83c20SShannon Nelson 	i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__);
1125e4c83c20SShannon Nelson 	memset(&cmd_details, 0, sizeof(cmd_details));
1126e4c83c20SShannon Nelson 	cmd_details.wb_desc = &hw->nvm_wb_desc;
1127e4c83c20SShannon Nelson 
1128e4c83c20SShannon Nelson 	aq_desc_len = sizeof(struct i40e_aq_desc);
1129e4c83c20SShannon Nelson 	memset(&hw->nvm_wb_desc, 0, aq_desc_len);
1130e4c83c20SShannon Nelson 
1131e4c83c20SShannon Nelson 	/* get the aq descriptor */
1132e4c83c20SShannon Nelson 	if (cmd->data_size < aq_desc_len) {
1133e4c83c20SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
1134e4c83c20SShannon Nelson 			   "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n",
1135e4c83c20SShannon Nelson 			   cmd->data_size, aq_desc_len);
1136e4c83c20SShannon Nelson 		*perrno = -EINVAL;
1137e4c83c20SShannon Nelson 		return I40E_ERR_PARAM;
1138e4c83c20SShannon Nelson 	}
1139e4c83c20SShannon Nelson 	aq_desc = (struct i40e_aq_desc *)bytes;
1140e4c83c20SShannon Nelson 
1141e4c83c20SShannon Nelson 	/* if data buffer needed, make sure it's ready */
1142e4c83c20SShannon Nelson 	aq_data_len = cmd->data_size - aq_desc_len;
1143e4c83c20SShannon Nelson 	buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen));
1144e4c83c20SShannon Nelson 	if (buff_size) {
1145e4c83c20SShannon Nelson 		if (!hw->nvm_buff.va) {
1146e4c83c20SShannon Nelson 			status = i40e_allocate_virt_mem(hw, &hw->nvm_buff,
1147e4c83c20SShannon Nelson 							hw->aq.asq_buf_size);
1148e4c83c20SShannon Nelson 			if (status)
1149e4c83c20SShannon Nelson 				i40e_debug(hw, I40E_DEBUG_NVM,
1150e4c83c20SShannon Nelson 					   "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n",
1151e4c83c20SShannon Nelson 					   status);
1152e4c83c20SShannon Nelson 		}
1153e4c83c20SShannon Nelson 
1154e4c83c20SShannon Nelson 		if (hw->nvm_buff.va) {
1155e4c83c20SShannon Nelson 			buff = hw->nvm_buff.va;
1156e4c83c20SShannon Nelson 			memcpy(buff, &bytes[aq_desc_len], aq_data_len);
1157e4c83c20SShannon Nelson 		}
1158e4c83c20SShannon Nelson 	}
1159e4c83c20SShannon Nelson 
1160e4c83c20SShannon Nelson 	/* and away we go! */
1161e4c83c20SShannon Nelson 	status = i40e_asq_send_command(hw, aq_desc, buff,
1162e4c83c20SShannon Nelson 				       buff_size, &cmd_details);
1163e4c83c20SShannon Nelson 	if (status) {
1164e4c83c20SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
1165e4c83c20SShannon Nelson 			   "i40e_nvmupd_exec_aq err %s aq_err %s\n",
1166e4c83c20SShannon Nelson 			   i40e_stat_str(hw, status),
1167e4c83c20SShannon Nelson 			   i40e_aq_str(hw, hw->aq.asq_last_status));
1168e4c83c20SShannon Nelson 		*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
1169e4c83c20SShannon Nelson 	}
1170e4c83c20SShannon Nelson 
1171e4c83c20SShannon Nelson 	return status;
1172e4c83c20SShannon Nelson }
1173e4c83c20SShannon Nelson 
1174e4c83c20SShannon Nelson /**
1175b72dc7b1SShannon Nelson  * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq
1176b72dc7b1SShannon Nelson  * @hw: pointer to hardware structure
1177b72dc7b1SShannon Nelson  * @cmd: pointer to nvm update command buffer
1178b72dc7b1SShannon Nelson  * @bytes: pointer to the data buffer
1179b72dc7b1SShannon Nelson  * @perrno: pointer to return error code
1180b72dc7b1SShannon Nelson  *
1181b72dc7b1SShannon Nelson  * cmd structure contains identifiers and data buffer
1182b72dc7b1SShannon Nelson  **/
1183b72dc7b1SShannon Nelson static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw,
1184b72dc7b1SShannon Nelson 					     struct i40e_nvm_access *cmd,
1185b72dc7b1SShannon Nelson 					     u8 *bytes, int *perrno)
1186b72dc7b1SShannon Nelson {
1187b72dc7b1SShannon Nelson 	u32 aq_total_len;
1188b72dc7b1SShannon Nelson 	u32 aq_desc_len;
1189b72dc7b1SShannon Nelson 	int remainder;
1190b72dc7b1SShannon Nelson 	u8 *buff;
1191b72dc7b1SShannon Nelson 
1192b72dc7b1SShannon Nelson 	i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__);
1193b72dc7b1SShannon Nelson 
1194b72dc7b1SShannon Nelson 	aq_desc_len = sizeof(struct i40e_aq_desc);
1195b72dc7b1SShannon Nelson 	aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen);
1196b72dc7b1SShannon Nelson 
1197b72dc7b1SShannon Nelson 	/* check offset range */
1198b72dc7b1SShannon Nelson 	if (cmd->offset > aq_total_len) {
1199b72dc7b1SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n",
1200b72dc7b1SShannon Nelson 			   __func__, cmd->offset, aq_total_len);
1201b72dc7b1SShannon Nelson 		*perrno = -EINVAL;
1202b72dc7b1SShannon Nelson 		return I40E_ERR_PARAM;
1203b72dc7b1SShannon Nelson 	}
1204b72dc7b1SShannon Nelson 
1205b72dc7b1SShannon Nelson 	/* check copylength range */
1206b72dc7b1SShannon Nelson 	if (cmd->data_size > (aq_total_len - cmd->offset)) {
1207b72dc7b1SShannon Nelson 		int new_len = aq_total_len - cmd->offset;
1208b72dc7b1SShannon Nelson 
1209b72dc7b1SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n",
1210b72dc7b1SShannon Nelson 			   __func__, cmd->data_size, new_len);
1211b72dc7b1SShannon Nelson 		cmd->data_size = new_len;
1212b72dc7b1SShannon Nelson 	}
1213b72dc7b1SShannon Nelson 
1214b72dc7b1SShannon Nelson 	remainder = cmd->data_size;
1215b72dc7b1SShannon Nelson 	if (cmd->offset < aq_desc_len) {
1216b72dc7b1SShannon Nelson 		u32 len = aq_desc_len - cmd->offset;
1217b72dc7b1SShannon Nelson 
1218b72dc7b1SShannon Nelson 		len = min(len, cmd->data_size);
1219b72dc7b1SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n",
1220b72dc7b1SShannon Nelson 			   __func__, cmd->offset, cmd->offset + len);
1221b72dc7b1SShannon Nelson 
1222b72dc7b1SShannon Nelson 		buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset;
1223b72dc7b1SShannon Nelson 		memcpy(bytes, buff, len);
1224b72dc7b1SShannon Nelson 
1225b72dc7b1SShannon Nelson 		bytes += len;
1226b72dc7b1SShannon Nelson 		remainder -= len;
1227b72dc7b1SShannon Nelson 		buff = hw->nvm_buff.va;
1228b72dc7b1SShannon Nelson 	} else {
1229b72dc7b1SShannon Nelson 		buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len);
1230b72dc7b1SShannon Nelson 	}
1231b72dc7b1SShannon Nelson 
1232b72dc7b1SShannon Nelson 	if (remainder > 0) {
1233b72dc7b1SShannon Nelson 		int start_byte = buff - (u8 *)hw->nvm_buff.va;
1234b72dc7b1SShannon Nelson 
1235b72dc7b1SShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n",
1236b72dc7b1SShannon Nelson 			   __func__, start_byte, start_byte + remainder);
1237b72dc7b1SShannon Nelson 		memcpy(bytes, buff, remainder);
1238b72dc7b1SShannon Nelson 	}
1239b72dc7b1SShannon Nelson 
1240b72dc7b1SShannon Nelson 	return 0;
1241b72dc7b1SShannon Nelson }
1242b72dc7b1SShannon Nelson 
1243b72dc7b1SShannon Nelson /**
1244cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_read - Read NVM
1245cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
1246cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
1247cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
124879afe839SShannon Nelson  * @perrno: pointer to return error code
1249cd552cb4SShannon Nelson  *
1250cd552cb4SShannon Nelson  * cmd structure contains identifiers and data buffer
1251cd552cb4SShannon Nelson  **/
1252cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
1253cd552cb4SShannon Nelson 					struct i40e_nvm_access *cmd,
125479afe839SShannon Nelson 					u8 *bytes, int *perrno)
1255cd552cb4SShannon Nelson {
12566b5c1b89SShannon Nelson 	struct i40e_asq_cmd_details cmd_details;
1257cd552cb4SShannon Nelson 	i40e_status status;
1258cd552cb4SShannon Nelson 	u8 module, transaction;
1259cd552cb4SShannon Nelson 	bool last;
1260cd552cb4SShannon Nelson 
1261cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
1262cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
1263cd552cb4SShannon Nelson 	last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA);
1264cd552cb4SShannon Nelson 
12656b5c1b89SShannon Nelson 	memset(&cmd_details, 0, sizeof(cmd_details));
12666b5c1b89SShannon Nelson 	cmd_details.wb_desc = &hw->nvm_wb_desc;
12676b5c1b89SShannon Nelson 
1268cd552cb4SShannon Nelson 	status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
12696b5c1b89SShannon Nelson 				  bytes, last, &cmd_details);
127074d0d0edSShannon Nelson 	if (status) {
127174d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
127274d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_read mod 0x%x  off 0x%x  len 0x%x\n",
127374d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
127474d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
127574d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_read status %d aq %d\n",
127674d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
127779afe839SShannon Nelson 		*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
127874d0d0edSShannon Nelson 	}
1279cd552cb4SShannon Nelson 
1280cd552cb4SShannon Nelson 	return status;
1281cd552cb4SShannon Nelson }
1282cd552cb4SShannon Nelson 
1283cd552cb4SShannon Nelson /**
1284cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_erase - Erase an NVM module
1285cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
1286cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
128779afe839SShannon Nelson  * @perrno: pointer to return error code
1288cd552cb4SShannon Nelson  *
1289cd552cb4SShannon Nelson  * module, offset, data_size and data are in cmd structure
1290cd552cb4SShannon Nelson  **/
1291cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
1292cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
129379afe839SShannon Nelson 					 int *perrno)
1294cd552cb4SShannon Nelson {
1295cd552cb4SShannon Nelson 	i40e_status status = 0;
12966b5c1b89SShannon Nelson 	struct i40e_asq_cmd_details cmd_details;
1297cd552cb4SShannon Nelson 	u8 module, transaction;
1298cd552cb4SShannon Nelson 	bool last;
1299cd552cb4SShannon Nelson 
1300cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
1301cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
1302cd552cb4SShannon Nelson 	last = (transaction & I40E_NVM_LCB);
13036b5c1b89SShannon Nelson 
13046b5c1b89SShannon Nelson 	memset(&cmd_details, 0, sizeof(cmd_details));
13056b5c1b89SShannon Nelson 	cmd_details.wb_desc = &hw->nvm_wb_desc;
13066b5c1b89SShannon Nelson 
1307cd552cb4SShannon Nelson 	status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
13086b5c1b89SShannon Nelson 				   last, &cmd_details);
130974d0d0edSShannon Nelson 	if (status) {
131074d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
131174d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_erase mod 0x%x  off 0x%x len 0x%x\n",
131274d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
131374d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
131474d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_erase status %d aq %d\n",
131574d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
131679afe839SShannon Nelson 		*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
131774d0d0edSShannon Nelson 	}
1318cd552cb4SShannon Nelson 
1319cd552cb4SShannon Nelson 	return status;
1320cd552cb4SShannon Nelson }
1321cd552cb4SShannon Nelson 
1322cd552cb4SShannon Nelson /**
1323cd552cb4SShannon Nelson  * i40e_nvmupd_nvm_write - Write NVM
1324cd552cb4SShannon Nelson  * @hw: pointer to hardware structure
1325cd552cb4SShannon Nelson  * @cmd: pointer to nvm update command buffer
1326cd552cb4SShannon Nelson  * @bytes: pointer to the data buffer
132779afe839SShannon Nelson  * @perrno: pointer to return error code
1328cd552cb4SShannon Nelson  *
1329cd552cb4SShannon Nelson  * module, offset, data_size and data are in cmd structure
1330cd552cb4SShannon Nelson  **/
1331cd552cb4SShannon Nelson static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
1332cd552cb4SShannon Nelson 					 struct i40e_nvm_access *cmd,
133379afe839SShannon Nelson 					 u8 *bytes, int *perrno)
1334cd552cb4SShannon Nelson {
1335cd552cb4SShannon Nelson 	i40e_status status = 0;
13366b5c1b89SShannon Nelson 	struct i40e_asq_cmd_details cmd_details;
1337cd552cb4SShannon Nelson 	u8 module, transaction;
1338cd552cb4SShannon Nelson 	bool last;
1339cd552cb4SShannon Nelson 
1340cd552cb4SShannon Nelson 	transaction = i40e_nvmupd_get_transaction(cmd->config);
1341cd552cb4SShannon Nelson 	module = i40e_nvmupd_get_module(cmd->config);
1342cd552cb4SShannon Nelson 	last = (transaction & I40E_NVM_LCB);
134374d0d0edSShannon Nelson 
13446b5c1b89SShannon Nelson 	memset(&cmd_details, 0, sizeof(cmd_details));
13456b5c1b89SShannon Nelson 	cmd_details.wb_desc = &hw->nvm_wb_desc;
13466b5c1b89SShannon Nelson 
1347cd552cb4SShannon Nelson 	status = i40e_aq_update_nvm(hw, module, cmd->offset,
13486b5c1b89SShannon Nelson 				    (u16)cmd->data_size, bytes, last,
13496b5c1b89SShannon Nelson 				    &cmd_details);
135074d0d0edSShannon Nelson 	if (status) {
135174d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
135274d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
135374d0d0edSShannon Nelson 			   module, cmd->offset, cmd->data_size);
135474d0d0edSShannon Nelson 		i40e_debug(hw, I40E_DEBUG_NVM,
135574d0d0edSShannon Nelson 			   "i40e_nvmupd_nvm_write status %d aq %d\n",
135674d0d0edSShannon Nelson 			   status, hw->aq.asq_last_status);
135779afe839SShannon Nelson 		*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
135874d0d0edSShannon Nelson 	}
1359cd552cb4SShannon Nelson 
1360cd552cb4SShannon Nelson 	return status;
1361cd552cb4SShannon Nelson }
1362