xref: /freebsd/sys/dev/e1000/e1000_i210.c (revision 1bbdc25fc1edb43562bf2a5f30df7381078991d4)
1ab5d0362SJack F Vogel /******************************************************************************
27282444bSPedro F. Giffuni   SPDX-License-Identifier: BSD-3-Clause
3ab5d0362SJack F Vogel 
4702cac6cSKevin Bowling   Copyright (c) 2001-2020, Intel Corporation
5ab5d0362SJack F Vogel   All rights reserved.
6ab5d0362SJack F Vogel 
7ab5d0362SJack F Vogel   Redistribution and use in source and binary forms, with or without
8ab5d0362SJack F Vogel   modification, are permitted provided that the following conditions are met:
9ab5d0362SJack F Vogel 
10ab5d0362SJack F Vogel    1. Redistributions of source code must retain the above copyright notice,
11ab5d0362SJack F Vogel       this list of conditions and the following disclaimer.
12ab5d0362SJack F Vogel 
13ab5d0362SJack F Vogel    2. Redistributions in binary form must reproduce the above copyright
14ab5d0362SJack F Vogel       notice, this list of conditions and the following disclaimer in the
15ab5d0362SJack F Vogel       documentation and/or other materials provided with the distribution.
16ab5d0362SJack F Vogel 
17ab5d0362SJack F Vogel    3. Neither the name of the Intel Corporation nor the names of its
18ab5d0362SJack F Vogel       contributors may be used to endorse or promote products derived from
19ab5d0362SJack F Vogel       this software without specific prior written permission.
20ab5d0362SJack F Vogel 
21ab5d0362SJack F Vogel   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22ab5d0362SJack F Vogel   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23ab5d0362SJack F Vogel   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24ab5d0362SJack F Vogel   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25ab5d0362SJack F Vogel   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26ab5d0362SJack F Vogel   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27ab5d0362SJack F Vogel   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28ab5d0362SJack F Vogel   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29ab5d0362SJack F Vogel   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30ab5d0362SJack F Vogel   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31ab5d0362SJack F Vogel   POSSIBILITY OF SUCH DAMAGE.
32ab5d0362SJack F Vogel 
33ab5d0362SJack F Vogel ******************************************************************************/
34ab5d0362SJack F Vogel /*$FreeBSD$*/
35ab5d0362SJack F Vogel 
36ab5d0362SJack F Vogel #include "e1000_api.h"
37ab5d0362SJack F Vogel 
38ab5d0362SJack F Vogel 
39ab5d0362SJack F Vogel static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw);
40ab5d0362SJack F Vogel static void e1000_release_nvm_i210(struct e1000_hw *hw);
41ab5d0362SJack F Vogel static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
42ab5d0362SJack F Vogel 				u16 *data);
43ab5d0362SJack F Vogel static s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw);
44ab5d0362SJack F Vogel static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data);
45ab5d0362SJack F Vogel 
46ab5d0362SJack F Vogel /**
47ab5d0362SJack F Vogel  *  e1000_acquire_nvm_i210 - Request for access to EEPROM
48ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
49ab5d0362SJack F Vogel  *
50ab5d0362SJack F Vogel  *  Acquire the necessary semaphores for exclusive access to the EEPROM.
51ab5d0362SJack F Vogel  *  Set the EEPROM access request bit and wait for EEPROM access grant bit.
52ab5d0362SJack F Vogel  *  Return successful if access grant bit set, else clear the request for
53ab5d0362SJack F Vogel  *  EEPROM access and return -E1000_ERR_NVM (-1).
54ab5d0362SJack F Vogel  **/
55ab5d0362SJack F Vogel static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw)
56ab5d0362SJack F Vogel {
57ab5d0362SJack F Vogel 	s32 ret_val;
58ab5d0362SJack F Vogel 
59ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_acquire_nvm_i210");
60ab5d0362SJack F Vogel 
61d5210708SMatt Macy 	ret_val = e1000_acquire_swfw_sync(hw, E1000_SWFW_EEP_SM);
62ab5d0362SJack F Vogel 
63ab5d0362SJack F Vogel 	return ret_val;
64ab5d0362SJack F Vogel }
65ab5d0362SJack F Vogel 
66ab5d0362SJack F Vogel /**
67ab5d0362SJack F Vogel  *  e1000_release_nvm_i210 - Release exclusive access to EEPROM
68ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
69ab5d0362SJack F Vogel  *
70ab5d0362SJack F Vogel  *  Stop any current commands to the EEPROM and clear the EEPROM request bit,
71ab5d0362SJack F Vogel  *  then release the semaphores acquired.
72ab5d0362SJack F Vogel  **/
73ab5d0362SJack F Vogel static void e1000_release_nvm_i210(struct e1000_hw *hw)
74ab5d0362SJack F Vogel {
75ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_release_nvm_i210");
76ab5d0362SJack F Vogel 
77d5210708SMatt Macy 	e1000_release_swfw_sync(hw, E1000_SWFW_EEP_SM);
78ab5d0362SJack F Vogel }
79ab5d0362SJack F Vogel 
80ab5d0362SJack F Vogel /**
81ab5d0362SJack F Vogel  *  e1000_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register
82ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
83ab5d0362SJack F Vogel  *  @offset: offset of word in the Shadow Ram to read
84ab5d0362SJack F Vogel  *  @words: number of words to read
85ab5d0362SJack F Vogel  *  @data: word read from the Shadow Ram
86ab5d0362SJack F Vogel  *
87ab5d0362SJack F Vogel  *  Reads a 16 bit word from the Shadow Ram using the EERD register.
88ab5d0362SJack F Vogel  *  Uses necessary synchronization semaphores.
89ab5d0362SJack F Vogel  **/
90ab5d0362SJack F Vogel s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
91ab5d0362SJack F Vogel 			     u16 *data)
92ab5d0362SJack F Vogel {
93ab5d0362SJack F Vogel 	s32 status = E1000_SUCCESS;
94ab5d0362SJack F Vogel 	u16 i, count;
95ab5d0362SJack F Vogel 
96ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_read_nvm_srrd_i210");
97ab5d0362SJack F Vogel 
98ab5d0362SJack F Vogel 	/* We cannot hold synchronization semaphores for too long,
99ab5d0362SJack F Vogel 	 * because of forceful takeover procedure. However it is more efficient
100ab5d0362SJack F Vogel 	 * to read in bursts than synchronizing access for each word. */
101ab5d0362SJack F Vogel 	for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
102ab5d0362SJack F Vogel 		count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
103ab5d0362SJack F Vogel 			E1000_EERD_EEWR_MAX_COUNT : (words - i);
104ab5d0362SJack F Vogel 		if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
105ab5d0362SJack F Vogel 			status = e1000_read_nvm_eerd(hw, offset, count,
106ab5d0362SJack F Vogel 						     data + i);
107ab5d0362SJack F Vogel 			hw->nvm.ops.release(hw);
108ab5d0362SJack F Vogel 		} else {
109ab5d0362SJack F Vogel 			status = E1000_ERR_SWFW_SYNC;
110ab5d0362SJack F Vogel 		}
111ab5d0362SJack F Vogel 
112ab5d0362SJack F Vogel 		if (status != E1000_SUCCESS)
113ab5d0362SJack F Vogel 			break;
114ab5d0362SJack F Vogel 	}
115ab5d0362SJack F Vogel 
116ab5d0362SJack F Vogel 	return status;
117ab5d0362SJack F Vogel }
118ab5d0362SJack F Vogel 
119ab5d0362SJack F Vogel /**
120ab5d0362SJack F Vogel  *  e1000_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR
121ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
122ab5d0362SJack F Vogel  *  @offset: offset within the Shadow RAM to be written to
123ab5d0362SJack F Vogel  *  @words: number of words to write
124ab5d0362SJack F Vogel  *  @data: 16 bit word(s) to be written to the Shadow RAM
125ab5d0362SJack F Vogel  *
126ab5d0362SJack F Vogel  *  Writes data to Shadow RAM at offset using EEWR register.
127ab5d0362SJack F Vogel  *
128ab5d0362SJack F Vogel  *  If e1000_update_nvm_checksum is not called after this function , the
129ab5d0362SJack F Vogel  *  data will not be committed to FLASH and also Shadow RAM will most likely
130ab5d0362SJack F Vogel  *  contain an invalid checksum.
131ab5d0362SJack F Vogel  *
132ab5d0362SJack F Vogel  *  If error code is returned, data and Shadow RAM may be inconsistent - buffer
133ab5d0362SJack F Vogel  *  partially written.
134ab5d0362SJack F Vogel  **/
135ab5d0362SJack F Vogel s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
136ab5d0362SJack F Vogel 			      u16 *data)
137ab5d0362SJack F Vogel {
138ab5d0362SJack F Vogel 	s32 status = E1000_SUCCESS;
139ab5d0362SJack F Vogel 	u16 i, count;
140ab5d0362SJack F Vogel 
141ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_write_nvm_srwr_i210");
142ab5d0362SJack F Vogel 
143ab5d0362SJack F Vogel 	/* We cannot hold synchronization semaphores for too long,
144ab5d0362SJack F Vogel 	 * because of forceful takeover procedure. However it is more efficient
145ab5d0362SJack F Vogel 	 * to write in bursts than synchronizing access for each word. */
146ab5d0362SJack F Vogel 	for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
147ab5d0362SJack F Vogel 		count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
148ab5d0362SJack F Vogel 			E1000_EERD_EEWR_MAX_COUNT : (words - i);
149ab5d0362SJack F Vogel 		if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
150ab5d0362SJack F Vogel 			status = e1000_write_nvm_srwr(hw, offset, count,
151ab5d0362SJack F Vogel 						      data + i);
152ab5d0362SJack F Vogel 			hw->nvm.ops.release(hw);
153ab5d0362SJack F Vogel 		} else {
154ab5d0362SJack F Vogel 			status = E1000_ERR_SWFW_SYNC;
155ab5d0362SJack F Vogel 		}
156ab5d0362SJack F Vogel 
157ab5d0362SJack F Vogel 		if (status != E1000_SUCCESS)
158ab5d0362SJack F Vogel 			break;
159ab5d0362SJack F Vogel 	}
160ab5d0362SJack F Vogel 
161ab5d0362SJack F Vogel 	return status;
162ab5d0362SJack F Vogel }
163ab5d0362SJack F Vogel 
164ab5d0362SJack F Vogel /**
165ab5d0362SJack F Vogel  *  e1000_write_nvm_srwr - Write to Shadow Ram using EEWR
166ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
167ab5d0362SJack F Vogel  *  @offset: offset within the Shadow Ram to be written to
168ab5d0362SJack F Vogel  *  @words: number of words to write
169ab5d0362SJack F Vogel  *  @data: 16 bit word(s) to be written to the Shadow Ram
170ab5d0362SJack F Vogel  *
171ab5d0362SJack F Vogel  *  Writes data to Shadow Ram at offset using EEWR register.
172ab5d0362SJack F Vogel  *
173ab5d0362SJack F Vogel  *  If e1000_update_nvm_checksum is not called after this function , the
174ab5d0362SJack F Vogel  *  Shadow Ram will most likely contain an invalid checksum.
175ab5d0362SJack F Vogel  **/
176ab5d0362SJack F Vogel static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
177ab5d0362SJack F Vogel 				u16 *data)
178ab5d0362SJack F Vogel {
179ab5d0362SJack F Vogel 	struct e1000_nvm_info *nvm = &hw->nvm;
180ab5d0362SJack F Vogel 	u32 i, k, eewr = 0;
181ab5d0362SJack F Vogel 	u32 attempts = 100000;
182ab5d0362SJack F Vogel 	s32 ret_val = E1000_SUCCESS;
183ab5d0362SJack F Vogel 
184ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_write_nvm_srwr");
185ab5d0362SJack F Vogel 
186ab5d0362SJack F Vogel 	/*
187ab5d0362SJack F Vogel 	 * A check for invalid values:  offset too large, too many words,
188ab5d0362SJack F Vogel 	 * too many words for the offset, and not enough words.
189ab5d0362SJack F Vogel 	 */
190ab5d0362SJack F Vogel 	if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
191ab5d0362SJack F Vogel 	    (words == 0)) {
192ab5d0362SJack F Vogel 		DEBUGOUT("nvm parameter(s) out of bounds\n");
193ab5d0362SJack F Vogel 		ret_val = -E1000_ERR_NVM;
194ab5d0362SJack F Vogel 		goto out;
195ab5d0362SJack F Vogel 	}
196ab5d0362SJack F Vogel 
197ab5d0362SJack F Vogel 	for (i = 0; i < words; i++) {
198f6517a7eSChengwen Feng 		ret_val = -E1000_ERR_NVM;
199f6517a7eSChengwen Feng 
200ab5d0362SJack F Vogel 		eewr = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) |
201ab5d0362SJack F Vogel 			(data[i] << E1000_NVM_RW_REG_DATA) |
202ab5d0362SJack F Vogel 			E1000_NVM_RW_REG_START;
203ab5d0362SJack F Vogel 
204ab5d0362SJack F Vogel 		E1000_WRITE_REG(hw, E1000_SRWR, eewr);
205ab5d0362SJack F Vogel 
206ab5d0362SJack F Vogel 		for (k = 0; k < attempts; k++) {
207ab5d0362SJack F Vogel 			if (E1000_NVM_RW_REG_DONE &
208ab5d0362SJack F Vogel 			    E1000_READ_REG(hw, E1000_SRWR)) {
209ab5d0362SJack F Vogel 				ret_val = E1000_SUCCESS;
210ab5d0362SJack F Vogel 				break;
211ab5d0362SJack F Vogel 			}
212ab5d0362SJack F Vogel 			usec_delay(5);
213ab5d0362SJack F Vogel 		}
214ab5d0362SJack F Vogel 
215ab5d0362SJack F Vogel 		if (ret_val != E1000_SUCCESS) {
216ab5d0362SJack F Vogel 			DEBUGOUT("Shadow RAM write EEWR timed out\n");
217ab5d0362SJack F Vogel 			break;
218ab5d0362SJack F Vogel 		}
219ab5d0362SJack F Vogel 	}
220ab5d0362SJack F Vogel 
221ab5d0362SJack F Vogel out:
222ab5d0362SJack F Vogel 	return ret_val;
223ab5d0362SJack F Vogel }
224ab5d0362SJack F Vogel 
2257609433eSJack F Vogel /** e1000_read_invm_word_i210 - Reads OTP
226ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
227ab5d0362SJack F Vogel  *  @address: the word address (aka eeprom offset) to read
228ab5d0362SJack F Vogel  *  @data: pointer to the data read
229ab5d0362SJack F Vogel  *
230ab5d0362SJack F Vogel  *  Reads 16-bit words from the OTP. Return error when the word is not
231ab5d0362SJack F Vogel  *  stored in OTP.
232ab5d0362SJack F Vogel  **/
2337609433eSJack F Vogel static s32 e1000_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data)
234ab5d0362SJack F Vogel {
235ab5d0362SJack F Vogel 	s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND;
236ab5d0362SJack F Vogel 	u32 invm_dword;
237ab5d0362SJack F Vogel 	u16 i;
238ab5d0362SJack F Vogel 	u8 record_type, word_address;
239ab5d0362SJack F Vogel 
2407609433eSJack F Vogel 	DEBUGFUNC("e1000_read_invm_word_i210");
241ab5d0362SJack F Vogel 
242ab5d0362SJack F Vogel 	for (i = 0; i < E1000_INVM_SIZE; i++) {
243ab5d0362SJack F Vogel 		invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i));
244ab5d0362SJack F Vogel 		/* Get record type */
245ab5d0362SJack F Vogel 		record_type = INVM_DWORD_TO_RECORD_TYPE(invm_dword);
246ab5d0362SJack F Vogel 		if (record_type == E1000_INVM_UNINITIALIZED_STRUCTURE)
247ab5d0362SJack F Vogel 			break;
248ab5d0362SJack F Vogel 		if (record_type == E1000_INVM_CSR_AUTOLOAD_STRUCTURE)
249ab5d0362SJack F Vogel 			i += E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS;
250ab5d0362SJack F Vogel 		if (record_type == E1000_INVM_RSA_KEY_SHA256_STRUCTURE)
251ab5d0362SJack F Vogel 			i += E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS;
252ab5d0362SJack F Vogel 		if (record_type == E1000_INVM_WORD_AUTOLOAD_STRUCTURE) {
253ab5d0362SJack F Vogel 			word_address = INVM_DWORD_TO_WORD_ADDRESS(invm_dword);
254ab5d0362SJack F Vogel 			if (word_address == address) {
255ab5d0362SJack F Vogel 				*data = INVM_DWORD_TO_WORD_DATA(invm_dword);
256ab5d0362SJack F Vogel 				DEBUGOUT2("Read INVM Word 0x%02x = %x",
257ab5d0362SJack F Vogel 					  address, *data);
258ab5d0362SJack F Vogel 				status = E1000_SUCCESS;
259ab5d0362SJack F Vogel 				break;
260ab5d0362SJack F Vogel 			}
261ab5d0362SJack F Vogel 		}
262ab5d0362SJack F Vogel 	}
263ab5d0362SJack F Vogel 	if (status != E1000_SUCCESS)
264ab5d0362SJack F Vogel 		DEBUGOUT1("Requested word 0x%02x not found in OTP\n", address);
265ab5d0362SJack F Vogel 	return status;
266ab5d0362SJack F Vogel }
267ab5d0362SJack F Vogel 
2687609433eSJack F Vogel /** e1000_read_invm_i210 - Read invm wrapper function for I210/I211
2697609433eSJack F Vogel  *  @hw: pointer to the HW structure
2707609433eSJack F Vogel  *  @address: the word address (aka eeprom offset) to read
2717609433eSJack F Vogel  *  @data: pointer to the data read
2727609433eSJack F Vogel  *
2737609433eSJack F Vogel  *  Wrapper function to return data formerly found in the NVM.
2747609433eSJack F Vogel  **/
2757609433eSJack F Vogel static s32 e1000_read_invm_i210(struct e1000_hw *hw, u16 offset,
2767609433eSJack F Vogel 				u16 E1000_UNUSEDARG words, u16 *data)
2777609433eSJack F Vogel {
2787609433eSJack F Vogel 	s32 ret_val = E1000_SUCCESS;
2797609433eSJack F Vogel 
2807609433eSJack F Vogel 	DEBUGFUNC("e1000_read_invm_i210");
2817609433eSJack F Vogel 
2827609433eSJack F Vogel 	/* Only the MAC addr is required to be present in the iNVM */
2837609433eSJack F Vogel 	switch (offset) {
2847609433eSJack F Vogel 	case NVM_MAC_ADDR:
2857609433eSJack F Vogel 		ret_val = e1000_read_invm_word_i210(hw, (u8)offset, &data[0]);
2867609433eSJack F Vogel 		ret_val |= e1000_read_invm_word_i210(hw, (u8)offset + 1,
2877609433eSJack F Vogel 						     &data[1]);
2887609433eSJack F Vogel 		ret_val |= e1000_read_invm_word_i210(hw, (u8)offset + 2,
2897609433eSJack F Vogel 						     &data[2]);
2907609433eSJack F Vogel 		if (ret_val != E1000_SUCCESS)
2917609433eSJack F Vogel 			DEBUGOUT("MAC Addr not found in iNVM\n");
2927609433eSJack F Vogel 		break;
2937609433eSJack F Vogel 	case NVM_INIT_CTRL_2:
2947609433eSJack F Vogel 		ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data);
2957609433eSJack F Vogel 		if (ret_val != E1000_SUCCESS) {
2967609433eSJack F Vogel 			*data = NVM_INIT_CTRL_2_DEFAULT_I211;
2977609433eSJack F Vogel 			ret_val = E1000_SUCCESS;
2987609433eSJack F Vogel 		}
2997609433eSJack F Vogel 		break;
3007609433eSJack F Vogel 	case NVM_INIT_CTRL_4:
3017609433eSJack F Vogel 		ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data);
3027609433eSJack F Vogel 		if (ret_val != E1000_SUCCESS) {
3037609433eSJack F Vogel 			*data = NVM_INIT_CTRL_4_DEFAULT_I211;
3047609433eSJack F Vogel 			ret_val = E1000_SUCCESS;
3057609433eSJack F Vogel 		}
3067609433eSJack F Vogel 		break;
3077609433eSJack F Vogel 	case NVM_LED_1_CFG:
3087609433eSJack F Vogel 		ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data);
3097609433eSJack F Vogel 		if (ret_val != E1000_SUCCESS) {
3107609433eSJack F Vogel 			*data = NVM_LED_1_CFG_DEFAULT_I211;
3117609433eSJack F Vogel 			ret_val = E1000_SUCCESS;
3127609433eSJack F Vogel 		}
3137609433eSJack F Vogel 		break;
3147609433eSJack F Vogel 	case NVM_LED_0_2_CFG:
3157609433eSJack F Vogel 		ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data);
3167609433eSJack F Vogel 		if (ret_val != E1000_SUCCESS) {
3177609433eSJack F Vogel 			*data = NVM_LED_0_2_CFG_DEFAULT_I211;
3187609433eSJack F Vogel 			ret_val = E1000_SUCCESS;
3197609433eSJack F Vogel 		}
3207609433eSJack F Vogel 		break;
3217609433eSJack F Vogel 	case NVM_ID_LED_SETTINGS:
3227609433eSJack F Vogel 		ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data);
3237609433eSJack F Vogel 		if (ret_val != E1000_SUCCESS) {
3247609433eSJack F Vogel 			*data = ID_LED_RESERVED_FFFF;
3257609433eSJack F Vogel 			ret_val = E1000_SUCCESS;
3267609433eSJack F Vogel 		}
3277609433eSJack F Vogel 		break;
3287609433eSJack F Vogel 	case NVM_SUB_DEV_ID:
3297609433eSJack F Vogel 		*data = hw->subsystem_device_id;
3307609433eSJack F Vogel 		break;
3317609433eSJack F Vogel 	case NVM_SUB_VEN_ID:
3327609433eSJack F Vogel 		*data = hw->subsystem_vendor_id;
3337609433eSJack F Vogel 		break;
3347609433eSJack F Vogel 	case NVM_DEV_ID:
3357609433eSJack F Vogel 		*data = hw->device_id;
3367609433eSJack F Vogel 		break;
3377609433eSJack F Vogel 	case NVM_VEN_ID:
3387609433eSJack F Vogel 		*data = hw->vendor_id;
3397609433eSJack F Vogel 		break;
3407609433eSJack F Vogel 	default:
3417609433eSJack F Vogel 		DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset);
3427609433eSJack F Vogel 		*data = NVM_RESERVED_WORD;
3437609433eSJack F Vogel 		break;
3447609433eSJack F Vogel 	}
3457609433eSJack F Vogel 	return ret_val;
3467609433eSJack F Vogel }
3477609433eSJack F Vogel 
348ab5d0362SJack F Vogel /**
349984d1616SKevin Bowling  *  e1000_read_invm_version - Reads iNVM version and image type
350984d1616SKevin Bowling  *  @hw: pointer to the HW structure
351984d1616SKevin Bowling  *  @invm_ver: version structure for the version read
352984d1616SKevin Bowling  *
353984d1616SKevin Bowling  *  Reads iNVM version and image type.
354984d1616SKevin Bowling  **/
355984d1616SKevin Bowling s32 e1000_read_invm_version(struct e1000_hw *hw,
356984d1616SKevin Bowling 			    struct e1000_fw_version *invm_ver)
357984d1616SKevin Bowling {
358984d1616SKevin Bowling 	u32 *record = NULL;
359984d1616SKevin Bowling 	u32 *next_record = NULL;
360984d1616SKevin Bowling 	u32 i = 0;
361984d1616SKevin Bowling 	u32 invm_dword = 0;
362984d1616SKevin Bowling 	u32 invm_blocks = E1000_INVM_SIZE - (E1000_INVM_ULT_BYTES_SIZE /
363984d1616SKevin Bowling 					     E1000_INVM_RECORD_SIZE_IN_BYTES);
364984d1616SKevin Bowling 	u32 buffer[E1000_INVM_SIZE];
365984d1616SKevin Bowling 	s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND;
366984d1616SKevin Bowling 	u16 version = 0;
367984d1616SKevin Bowling 
368984d1616SKevin Bowling 	DEBUGFUNC("e1000_read_invm_version");
369984d1616SKevin Bowling 
370984d1616SKevin Bowling 	/* Read iNVM memory */
371984d1616SKevin Bowling 	for (i = 0; i < E1000_INVM_SIZE; i++) {
372984d1616SKevin Bowling 		invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i));
373984d1616SKevin Bowling 		buffer[i] = invm_dword;
374984d1616SKevin Bowling 	}
375984d1616SKevin Bowling 
376984d1616SKevin Bowling 	/* Read version number */
377984d1616SKevin Bowling 	for (i = 1; i < invm_blocks; i++) {
378984d1616SKevin Bowling 		record = &buffer[invm_blocks - i];
379984d1616SKevin Bowling 		next_record = &buffer[invm_blocks - i + 1];
380984d1616SKevin Bowling 
381984d1616SKevin Bowling 		/* Check if we have first version location used */
382984d1616SKevin Bowling 		if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) {
383984d1616SKevin Bowling 			version = 0;
384984d1616SKevin Bowling 			status = E1000_SUCCESS;
385984d1616SKevin Bowling 			break;
386984d1616SKevin Bowling 		}
387984d1616SKevin Bowling 		/* Check if we have second version location used */
388984d1616SKevin Bowling 		else if ((i == 1) &&
389984d1616SKevin Bowling 			 ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) {
390984d1616SKevin Bowling 			version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
391984d1616SKevin Bowling 			status = E1000_SUCCESS;
392984d1616SKevin Bowling 			break;
393984d1616SKevin Bowling 		}
394984d1616SKevin Bowling 		/*
395984d1616SKevin Bowling 		 * Check if we have odd version location
396984d1616SKevin Bowling 		 * used and it is the last one used
397984d1616SKevin Bowling 		 */
398984d1616SKevin Bowling 		else if ((((*record & E1000_INVM_VER_FIELD_ONE) == 0) &&
399984d1616SKevin Bowling 			 ((*record & 0x3) == 0)) || (((*record & 0x3) != 0) &&
400984d1616SKevin Bowling 			 (i != 1))) {
401984d1616SKevin Bowling 			version = (*next_record & E1000_INVM_VER_FIELD_TWO)
402984d1616SKevin Bowling 				  >> 13;
403984d1616SKevin Bowling 			status = E1000_SUCCESS;
404984d1616SKevin Bowling 			break;
405984d1616SKevin Bowling 		}
406984d1616SKevin Bowling 		/*
407984d1616SKevin Bowling 		 * Check if we have even version location
408984d1616SKevin Bowling 		 * used and it is the last one used
409984d1616SKevin Bowling 		 */
410984d1616SKevin Bowling 		else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) &&
411984d1616SKevin Bowling 			 ((*record & 0x3) == 0)) {
412984d1616SKevin Bowling 			version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
413984d1616SKevin Bowling 			status = E1000_SUCCESS;
414984d1616SKevin Bowling 			break;
415984d1616SKevin Bowling 		}
416984d1616SKevin Bowling 	}
417984d1616SKevin Bowling 
418984d1616SKevin Bowling 	if (status == E1000_SUCCESS) {
419984d1616SKevin Bowling 		invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK)
420984d1616SKevin Bowling 					>> E1000_INVM_MAJOR_SHIFT;
421984d1616SKevin Bowling 		invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK;
422984d1616SKevin Bowling 	}
423984d1616SKevin Bowling 	/* Read Image Type */
424984d1616SKevin Bowling 	for (i = 1; i < invm_blocks; i++) {
425984d1616SKevin Bowling 		record = &buffer[invm_blocks - i];
426984d1616SKevin Bowling 		next_record = &buffer[invm_blocks - i + 1];
427984d1616SKevin Bowling 
428984d1616SKevin Bowling 		/* Check if we have image type in first location used */
429984d1616SKevin Bowling 		if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) {
430984d1616SKevin Bowling 			invm_ver->invm_img_type = 0;
431984d1616SKevin Bowling 			status = E1000_SUCCESS;
432984d1616SKevin Bowling 			break;
433984d1616SKevin Bowling 		}
434984d1616SKevin Bowling 		/* Check if we have image type in first location used */
435984d1616SKevin Bowling 		else if ((((*record & 0x3) == 0) &&
436984d1616SKevin Bowling 			 ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) ||
437984d1616SKevin Bowling 			 ((((*record & 0x3) != 0) && (i != 1)))) {
438984d1616SKevin Bowling 			invm_ver->invm_img_type =
439984d1616SKevin Bowling 				(*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23;
440984d1616SKevin Bowling 			status = E1000_SUCCESS;
441984d1616SKevin Bowling 			break;
442984d1616SKevin Bowling 		}
443984d1616SKevin Bowling 	}
444984d1616SKevin Bowling 	return status;
445984d1616SKevin Bowling }
446984d1616SKevin Bowling 
447984d1616SKevin Bowling /**
448ab5d0362SJack F Vogel  *  e1000_validate_nvm_checksum_i210 - Validate EEPROM checksum
449ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
450ab5d0362SJack F Vogel  *
451ab5d0362SJack F Vogel  *  Calculates the EEPROM checksum by reading/adding each word of the EEPROM
452ab5d0362SJack F Vogel  *  and then verifies that the sum of the EEPROM is equal to 0xBABA.
453ab5d0362SJack F Vogel  **/
454ab5d0362SJack F Vogel s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw)
455ab5d0362SJack F Vogel {
456ab5d0362SJack F Vogel 	s32 status = E1000_SUCCESS;
457ab5d0362SJack F Vogel 	s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *);
458ab5d0362SJack F Vogel 
459ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_validate_nvm_checksum_i210");
460ab5d0362SJack F Vogel 
461ab5d0362SJack F Vogel 	if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
462ab5d0362SJack F Vogel 
463ab5d0362SJack F Vogel 		/*
464ab5d0362SJack F Vogel 		 * Replace the read function with semaphore grabbing with
465ab5d0362SJack F Vogel 		 * the one that skips this for a while.
466ab5d0362SJack F Vogel 		 * We have semaphore taken already here.
467ab5d0362SJack F Vogel 		 */
468ab5d0362SJack F Vogel 		read_op_ptr = hw->nvm.ops.read;
469ab5d0362SJack F Vogel 		hw->nvm.ops.read = e1000_read_nvm_eerd;
470ab5d0362SJack F Vogel 
471ab5d0362SJack F Vogel 		status = e1000_validate_nvm_checksum_generic(hw);
472ab5d0362SJack F Vogel 
473ab5d0362SJack F Vogel 		/* Revert original read operation. */
474ab5d0362SJack F Vogel 		hw->nvm.ops.read = read_op_ptr;
475ab5d0362SJack F Vogel 
476ab5d0362SJack F Vogel 		hw->nvm.ops.release(hw);
477ab5d0362SJack F Vogel 	} else {
478ab5d0362SJack F Vogel 		status = E1000_ERR_SWFW_SYNC;
479ab5d0362SJack F Vogel 	}
480ab5d0362SJack F Vogel 
481ab5d0362SJack F Vogel 	return status;
482ab5d0362SJack F Vogel }
483ab5d0362SJack F Vogel 
484ab5d0362SJack F Vogel 
485ab5d0362SJack F Vogel /**
486ab5d0362SJack F Vogel  *  e1000_update_nvm_checksum_i210 - Update EEPROM checksum
487ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
488ab5d0362SJack F Vogel  *
489ab5d0362SJack F Vogel  *  Updates the EEPROM checksum by reading/adding each word of the EEPROM
490ab5d0362SJack F Vogel  *  up to the checksum.  Then calculates the EEPROM checksum and writes the
491ab5d0362SJack F Vogel  *  value to the EEPROM. Next commit EEPROM data onto the Flash.
492ab5d0362SJack F Vogel  **/
493ab5d0362SJack F Vogel s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw)
494ab5d0362SJack F Vogel {
4958cc64f1eSJack F Vogel 	s32 ret_val;
496ab5d0362SJack F Vogel 	u16 checksum = 0;
497ab5d0362SJack F Vogel 	u16 i, nvm_data;
498ab5d0362SJack F Vogel 
499ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_update_nvm_checksum_i210");
500ab5d0362SJack F Vogel 
501ab5d0362SJack F Vogel 	/*
502ab5d0362SJack F Vogel 	 * Read the first word from the EEPROM. If this times out or fails, do
503ab5d0362SJack F Vogel 	 * not continue or we could be in for a very long wait while every
504ab5d0362SJack F Vogel 	 * EEPROM read fails
505ab5d0362SJack F Vogel 	 */
506ab5d0362SJack F Vogel 	ret_val = e1000_read_nvm_eerd(hw, 0, 1, &nvm_data);
507ab5d0362SJack F Vogel 	if (ret_val != E1000_SUCCESS) {
508ab5d0362SJack F Vogel 		DEBUGOUT("EEPROM read failed\n");
509ab5d0362SJack F Vogel 		goto out;
510ab5d0362SJack F Vogel 	}
511ab5d0362SJack F Vogel 
512ab5d0362SJack F Vogel 	if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
513ab5d0362SJack F Vogel 		/*
514ab5d0362SJack F Vogel 		 * Do not use hw->nvm.ops.write, hw->nvm.ops.read
515ab5d0362SJack F Vogel 		 * because we do not want to take the synchronization
516ab5d0362SJack F Vogel 		 * semaphores twice here.
517ab5d0362SJack F Vogel 		 */
518ab5d0362SJack F Vogel 
519ab5d0362SJack F Vogel 		for (i = 0; i < NVM_CHECKSUM_REG; i++) {
520ab5d0362SJack F Vogel 			ret_val = e1000_read_nvm_eerd(hw, i, 1, &nvm_data);
521ab5d0362SJack F Vogel 			if (ret_val) {
522ab5d0362SJack F Vogel 				hw->nvm.ops.release(hw);
523ab5d0362SJack F Vogel 				DEBUGOUT("NVM Read Error while updating checksum.\n");
524ab5d0362SJack F Vogel 				goto out;
525ab5d0362SJack F Vogel 			}
526ab5d0362SJack F Vogel 			checksum += nvm_data;
527ab5d0362SJack F Vogel 		}
528ab5d0362SJack F Vogel 		checksum = (u16) NVM_SUM - checksum;
529ab5d0362SJack F Vogel 		ret_val = e1000_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
530ab5d0362SJack F Vogel 						&checksum);
531ab5d0362SJack F Vogel 		if (ret_val != E1000_SUCCESS) {
532ab5d0362SJack F Vogel 			hw->nvm.ops.release(hw);
533ab5d0362SJack F Vogel 			DEBUGOUT("NVM Write Error while updating checksum.\n");
534ab5d0362SJack F Vogel 			goto out;
535ab5d0362SJack F Vogel 		}
536ab5d0362SJack F Vogel 
537ab5d0362SJack F Vogel 		hw->nvm.ops.release(hw);
538ab5d0362SJack F Vogel 
539ab5d0362SJack F Vogel 		ret_val = e1000_update_flash_i210(hw);
540ab5d0362SJack F Vogel 	} else {
541ab5d0362SJack F Vogel 		ret_val = E1000_ERR_SWFW_SYNC;
542ab5d0362SJack F Vogel 	}
543ab5d0362SJack F Vogel out:
544ab5d0362SJack F Vogel 	return ret_val;
545ab5d0362SJack F Vogel }
546ab5d0362SJack F Vogel 
547ab5d0362SJack F Vogel /**
5487609433eSJack F Vogel  *  e1000_get_flash_presence_i210 - Check if flash device is detected.
5497609433eSJack F Vogel  *  @hw: pointer to the HW structure
5507609433eSJack F Vogel  *
5517609433eSJack F Vogel  **/
5527609433eSJack F Vogel bool e1000_get_flash_presence_i210(struct e1000_hw *hw)
5537609433eSJack F Vogel {
5547609433eSJack F Vogel 	u32 eec = 0;
555*1bbdc25fSKevin Bowling 	bool ret_val = false;
5567609433eSJack F Vogel 
5577609433eSJack F Vogel 	DEBUGFUNC("e1000_get_flash_presence_i210");
5587609433eSJack F Vogel 
5597609433eSJack F Vogel 	eec = E1000_READ_REG(hw, E1000_EECD);
5607609433eSJack F Vogel 
5617609433eSJack F Vogel 	if (eec & E1000_EECD_FLASH_DETECTED_I210)
562*1bbdc25fSKevin Bowling 		ret_val = true;
5637609433eSJack F Vogel 
5647609433eSJack F Vogel 	return ret_val;
5657609433eSJack F Vogel }
5667609433eSJack F Vogel 
5677609433eSJack F Vogel /**
568ab5d0362SJack F Vogel  *  e1000_update_flash_i210 - Commit EEPROM to the flash
569ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
570ab5d0362SJack F Vogel  *
571ab5d0362SJack F Vogel  **/
572ab5d0362SJack F Vogel s32 e1000_update_flash_i210(struct e1000_hw *hw)
573ab5d0362SJack F Vogel {
5748cc64f1eSJack F Vogel 	s32 ret_val;
575ab5d0362SJack F Vogel 	u32 flup;
576ab5d0362SJack F Vogel 
577ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_update_flash_i210");
578ab5d0362SJack F Vogel 
579ab5d0362SJack F Vogel 	ret_val = e1000_pool_flash_update_done_i210(hw);
580ab5d0362SJack F Vogel 	if (ret_val == -E1000_ERR_NVM) {
581ab5d0362SJack F Vogel 		DEBUGOUT("Flash update time out\n");
582ab5d0362SJack F Vogel 		goto out;
583ab5d0362SJack F Vogel 	}
584ab5d0362SJack F Vogel 
585ab5d0362SJack F Vogel 	flup = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD_I210;
586ab5d0362SJack F Vogel 	E1000_WRITE_REG(hw, E1000_EECD, flup);
587ab5d0362SJack F Vogel 
588ab5d0362SJack F Vogel 	ret_val = e1000_pool_flash_update_done_i210(hw);
589ab5d0362SJack F Vogel 	if (ret_val == E1000_SUCCESS)
590ab5d0362SJack F Vogel 		DEBUGOUT("Flash update complete\n");
591ab5d0362SJack F Vogel 	else
592ab5d0362SJack F Vogel 		DEBUGOUT("Flash update time out\n");
593ab5d0362SJack F Vogel 
594ab5d0362SJack F Vogel out:
595ab5d0362SJack F Vogel 	return ret_val;
596ab5d0362SJack F Vogel }
597ab5d0362SJack F Vogel 
598ab5d0362SJack F Vogel /**
599ab5d0362SJack F Vogel  *  e1000_pool_flash_update_done_i210 - Pool FLUDONE status.
600ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
601ab5d0362SJack F Vogel  *
602ab5d0362SJack F Vogel  **/
603ab5d0362SJack F Vogel s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw)
604ab5d0362SJack F Vogel {
605ab5d0362SJack F Vogel 	s32 ret_val = -E1000_ERR_NVM;
606ab5d0362SJack F Vogel 	u32 i, reg;
607ab5d0362SJack F Vogel 
608ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_pool_flash_update_done_i210");
609ab5d0362SJack F Vogel 
610ab5d0362SJack F Vogel 	for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
611ab5d0362SJack F Vogel 		reg = E1000_READ_REG(hw, E1000_EECD);
612ab5d0362SJack F Vogel 		if (reg & E1000_EECD_FLUDONE_I210) {
613ab5d0362SJack F Vogel 			ret_val = E1000_SUCCESS;
614ab5d0362SJack F Vogel 			break;
615ab5d0362SJack F Vogel 		}
616ab5d0362SJack F Vogel 		usec_delay(5);
617ab5d0362SJack F Vogel 	}
618ab5d0362SJack F Vogel 
619ab5d0362SJack F Vogel 	return ret_val;
620ab5d0362SJack F Vogel }
621ab5d0362SJack F Vogel 
622ab5d0362SJack F Vogel /**
623ab5d0362SJack F Vogel  *  e1000_init_nvm_params_i210 - Initialize i210 NVM function pointers
624ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
625ab5d0362SJack F Vogel  *
6267609433eSJack F Vogel  *  Initialize the i210/i211 NVM parameters and function pointers.
627ab5d0362SJack F Vogel  **/
628ab5d0362SJack F Vogel static s32 e1000_init_nvm_params_i210(struct e1000_hw *hw)
629ab5d0362SJack F Vogel {
6308cc64f1eSJack F Vogel 	s32 ret_val;
631ab5d0362SJack F Vogel 	struct e1000_nvm_info *nvm = &hw->nvm;
632ab5d0362SJack F Vogel 
633ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_init_nvm_params_i210");
634ab5d0362SJack F Vogel 
635ab5d0362SJack F Vogel 	ret_val = e1000_init_nvm_params_82575(hw);
636ab5d0362SJack F Vogel 	nvm->ops.acquire = e1000_acquire_nvm_i210;
637ab5d0362SJack F Vogel 	nvm->ops.release = e1000_release_nvm_i210;
6387609433eSJack F Vogel 	nvm->ops.valid_led_default = e1000_valid_led_default_i210;
6397609433eSJack F Vogel 	if (e1000_get_flash_presence_i210(hw)) {
6407609433eSJack F Vogel 		hw->nvm.type = e1000_nvm_flash_hw;
641ab5d0362SJack F Vogel 		nvm->ops.read    = e1000_read_nvm_srrd_i210;
642ab5d0362SJack F Vogel 		nvm->ops.write   = e1000_write_nvm_srwr_i210;
643ab5d0362SJack F Vogel 		nvm->ops.validate = e1000_validate_nvm_checksum_i210;
644ab5d0362SJack F Vogel 		nvm->ops.update   = e1000_update_nvm_checksum_i210;
6457609433eSJack F Vogel 	} else {
6467609433eSJack F Vogel 		hw->nvm.type = e1000_nvm_invm;
6477609433eSJack F Vogel 		nvm->ops.read     = e1000_read_invm_i210;
648ab5d0362SJack F Vogel 		nvm->ops.write    = e1000_null_write_nvm;
649ab5d0362SJack F Vogel 		nvm->ops.validate = e1000_null_ops_generic;
650ab5d0362SJack F Vogel 		nvm->ops.update   = e1000_null_ops_generic;
6517609433eSJack F Vogel 	}
6527609433eSJack F Vogel 	return ret_val;
653ab5d0362SJack F Vogel }
654ab5d0362SJack F Vogel 
655ab5d0362SJack F Vogel /**
656ab5d0362SJack F Vogel  *  e1000_init_function_pointers_i210 - Init func ptrs.
657ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
658ab5d0362SJack F Vogel  *
659ab5d0362SJack F Vogel  *  Called to initialize all function pointers and parameters.
660ab5d0362SJack F Vogel  **/
661ab5d0362SJack F Vogel void e1000_init_function_pointers_i210(struct e1000_hw *hw)
662ab5d0362SJack F Vogel {
663ab5d0362SJack F Vogel 	e1000_init_function_pointers_82575(hw);
664ab5d0362SJack F Vogel 	hw->nvm.ops.init_params = e1000_init_nvm_params_i210;
665ab5d0362SJack F Vogel }
666ab5d0362SJack F Vogel 
667ab5d0362SJack F Vogel /**
668ab5d0362SJack F Vogel  *  e1000_valid_led_default_i210 - Verify a valid default LED config
669ab5d0362SJack F Vogel  *  @hw: pointer to the HW structure
670ab5d0362SJack F Vogel  *  @data: pointer to the NVM (EEPROM)
671ab5d0362SJack F Vogel  *
672ab5d0362SJack F Vogel  *  Read the EEPROM for the current default LED configuration.  If the
673ab5d0362SJack F Vogel  *  LED configuration is not valid, set to a valid LED configuration.
674ab5d0362SJack F Vogel  **/
675ab5d0362SJack F Vogel static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data)
676ab5d0362SJack F Vogel {
677ab5d0362SJack F Vogel 	s32 ret_val;
678ab5d0362SJack F Vogel 
679ab5d0362SJack F Vogel 	DEBUGFUNC("e1000_valid_led_default_i210");
680ab5d0362SJack F Vogel 
681ab5d0362SJack F Vogel 	ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data);
682ab5d0362SJack F Vogel 	if (ret_val) {
683ab5d0362SJack F Vogel 		DEBUGOUT("NVM Read Error\n");
684ab5d0362SJack F Vogel 		goto out;
685ab5d0362SJack F Vogel 	}
686ab5d0362SJack F Vogel 
687ab5d0362SJack F Vogel 	if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) {
688ab5d0362SJack F Vogel 		switch (hw->phy.media_type) {
689ab5d0362SJack F Vogel 		case e1000_media_type_internal_serdes:
690ab5d0362SJack F Vogel 			*data = ID_LED_DEFAULT_I210_SERDES;
691ab5d0362SJack F Vogel 			break;
692ab5d0362SJack F Vogel 		case e1000_media_type_copper:
693ab5d0362SJack F Vogel 		default:
694ab5d0362SJack F Vogel 			*data = ID_LED_DEFAULT_I210;
695ab5d0362SJack F Vogel 			break;
696ab5d0362SJack F Vogel 		}
697ab5d0362SJack F Vogel 	}
698ab5d0362SJack F Vogel out:
699ab5d0362SJack F Vogel 	return ret_val;
700ab5d0362SJack F Vogel }
7017609433eSJack F Vogel 
7027609433eSJack F Vogel /**
7038cc64f1eSJack F Vogel  * e1000_pll_workaround_i210
7048cc64f1eSJack F Vogel  * @hw: pointer to the HW structure
7058cc64f1eSJack F Vogel  *
7068cc64f1eSJack F Vogel  * Works around an errata in the PLL circuit where it occasionally
7078cc64f1eSJack F Vogel  * provides the wrong clock frequency after power up.
7088cc64f1eSJack F Vogel  **/
7098cc64f1eSJack F Vogel static s32 e1000_pll_workaround_i210(struct e1000_hw *hw)
7108cc64f1eSJack F Vogel {
7118cc64f1eSJack F Vogel 	s32 ret_val;
7128cc64f1eSJack F Vogel 	u32 wuc, mdicnfg, ctrl, ctrl_ext, reg_val;
7138cc64f1eSJack F Vogel 	u16 nvm_word, phy_word, pci_word, tmp_nvm;
7148cc64f1eSJack F Vogel 	int i;
7158cc64f1eSJack F Vogel 
7161883a6ffSGuinan Sun 	/* Get PHY semaphore */
7171883a6ffSGuinan Sun 	hw->phy.ops.acquire(hw);
7188cc64f1eSJack F Vogel 	/* Get and set needed register values */
7198cc64f1eSJack F Vogel 	wuc = E1000_READ_REG(hw, E1000_WUC);
7208cc64f1eSJack F Vogel 	mdicnfg = E1000_READ_REG(hw, E1000_MDICNFG);
7218cc64f1eSJack F Vogel 	reg_val = mdicnfg & ~E1000_MDICNFG_EXT_MDIO;
7228cc64f1eSJack F Vogel 	E1000_WRITE_REG(hw, E1000_MDICNFG, reg_val);
7238cc64f1eSJack F Vogel 
7248cc64f1eSJack F Vogel 	/* Get data from NVM, or set default */
7258cc64f1eSJack F Vogel 	ret_val = e1000_read_invm_word_i210(hw, E1000_INVM_AUTOLOAD,
7268cc64f1eSJack F Vogel 					    &nvm_word);
7278cc64f1eSJack F Vogel 	if (ret_val != E1000_SUCCESS)
7288cc64f1eSJack F Vogel 		nvm_word = E1000_INVM_DEFAULT_AL;
7298cc64f1eSJack F Vogel 	tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL;
730984d1616SKevin Bowling 	phy_word = E1000_PHY_PLL_UNCONF;
7318cc64f1eSJack F Vogel 	for (i = 0; i < E1000_MAX_PLL_TRIES; i++) {
7328cc64f1eSJack F Vogel 		/* check current state directly from internal PHY */
7331883a6ffSGuinan Sun 		e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, 0xFC);
7341883a6ffSGuinan Sun 		usec_delay(20);
7351883a6ffSGuinan Sun 		e1000_read_phy_reg_mdic(hw, E1000_PHY_PLL_FREQ_REG, &phy_word);
7361883a6ffSGuinan Sun 		usec_delay(20);
7371883a6ffSGuinan Sun 		e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, 0);
7388cc64f1eSJack F Vogel 		if ((phy_word & E1000_PHY_PLL_UNCONF)
7398cc64f1eSJack F Vogel 		    != E1000_PHY_PLL_UNCONF) {
7408cc64f1eSJack F Vogel 			ret_val = E1000_SUCCESS;
7418cc64f1eSJack F Vogel 			break;
7428cc64f1eSJack F Vogel 		} else {
7438cc64f1eSJack F Vogel 			ret_val = -E1000_ERR_PHY;
7448cc64f1eSJack F Vogel 		}
7458cc64f1eSJack F Vogel 		/* directly reset the internal PHY */
7468cc64f1eSJack F Vogel 		ctrl = E1000_READ_REG(hw, E1000_CTRL);
7478cc64f1eSJack F Vogel 		E1000_WRITE_REG(hw, E1000_CTRL, ctrl|E1000_CTRL_PHY_RST);
7488cc64f1eSJack F Vogel 
7498cc64f1eSJack F Vogel 		ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
7508cc64f1eSJack F Vogel 		ctrl_ext |= (E1000_CTRL_EXT_PHYPDEN | E1000_CTRL_EXT_SDLPE);
7518cc64f1eSJack F Vogel 		E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
7528cc64f1eSJack F Vogel 
7538cc64f1eSJack F Vogel 		E1000_WRITE_REG(hw, E1000_WUC, 0);
7548cc64f1eSJack F Vogel 		reg_val = (E1000_INVM_AUTOLOAD << 4) | (tmp_nvm << 16);
7558cc64f1eSJack F Vogel 		E1000_WRITE_REG(hw, E1000_EEARBC_I210, reg_val);
7568cc64f1eSJack F Vogel 
7578cc64f1eSJack F Vogel 		e1000_read_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
7588cc64f1eSJack F Vogel 		pci_word |= E1000_PCI_PMCSR_D3;
7598cc64f1eSJack F Vogel 		e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
7608cc64f1eSJack F Vogel 		msec_delay(1);
7618cc64f1eSJack F Vogel 		pci_word &= ~E1000_PCI_PMCSR_D3;
7628cc64f1eSJack F Vogel 		e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
7638cc64f1eSJack F Vogel 		reg_val = (E1000_INVM_AUTOLOAD << 4) | (nvm_word << 16);
7648cc64f1eSJack F Vogel 		E1000_WRITE_REG(hw, E1000_EEARBC_I210, reg_val);
7658cc64f1eSJack F Vogel 
7668cc64f1eSJack F Vogel 		/* restore WUC register */
7678cc64f1eSJack F Vogel 		E1000_WRITE_REG(hw, E1000_WUC, wuc);
7688cc64f1eSJack F Vogel 	}
7698cc64f1eSJack F Vogel 	/* restore MDICNFG setting */
7708cc64f1eSJack F Vogel 	E1000_WRITE_REG(hw, E1000_MDICNFG, mdicnfg);
7711883a6ffSGuinan Sun 	/* Release PHY semaphore */
7721883a6ffSGuinan Sun 	hw->phy.ops.release(hw);
7738cc64f1eSJack F Vogel 	return ret_val;
7748cc64f1eSJack F Vogel }
7758cc64f1eSJack F Vogel 
7768cc64f1eSJack F Vogel /**
777c80429ceSEric Joyner  *  e1000_get_cfg_done_i210 - Read config done bit
778c80429ceSEric Joyner  *  @hw: pointer to the HW structure
779c80429ceSEric Joyner  *
780c80429ceSEric Joyner  *  Read the management control register for the config done bit for
781c80429ceSEric Joyner  *  completion status.  NOTE: silicon which is EEPROM-less will fail trying
782c80429ceSEric Joyner  *  to read the config done bit, so an error is *ONLY* logged and returns
783c80429ceSEric Joyner  *  E1000_SUCCESS.  If we were to return with error, EEPROM-less silicon
784c80429ceSEric Joyner  *  would not be able to be reset or change link.
785c80429ceSEric Joyner  **/
786c80429ceSEric Joyner static s32 e1000_get_cfg_done_i210(struct e1000_hw *hw)
787c80429ceSEric Joyner {
788c80429ceSEric Joyner 	s32 timeout = PHY_CFG_TIMEOUT;
789c80429ceSEric Joyner 	u32 mask = E1000_NVM_CFG_DONE_PORT_0;
790c80429ceSEric Joyner 
791c80429ceSEric Joyner 	DEBUGFUNC("e1000_get_cfg_done_i210");
792c80429ceSEric Joyner 
793c80429ceSEric Joyner 	while (timeout) {
794c80429ceSEric Joyner 		if (E1000_READ_REG(hw, E1000_EEMNGCTL_I210) & mask)
795c80429ceSEric Joyner 			break;
796c80429ceSEric Joyner 		msec_delay(1);
797c80429ceSEric Joyner 		timeout--;
798c80429ceSEric Joyner 	}
799c80429ceSEric Joyner 	if (!timeout)
800c80429ceSEric Joyner 		DEBUGOUT("MNG configuration cycle has not completed.\n");
801c80429ceSEric Joyner 
802c80429ceSEric Joyner 	return E1000_SUCCESS;
803c80429ceSEric Joyner }
804c80429ceSEric Joyner 
805c80429ceSEric Joyner /**
8068cc64f1eSJack F Vogel  *  e1000_init_hw_i210 - Init hw for I210/I211
8078cc64f1eSJack F Vogel  *  @hw: pointer to the HW structure
8088cc64f1eSJack F Vogel  *
8098cc64f1eSJack F Vogel  *  Called to initialize hw for i210 hw family.
8108cc64f1eSJack F Vogel  **/
8118cc64f1eSJack F Vogel s32 e1000_init_hw_i210(struct e1000_hw *hw)
8128cc64f1eSJack F Vogel {
81351569bd7SEric Joyner 	struct e1000_mac_info *mac = &hw->mac;
8148cc64f1eSJack F Vogel 	s32 ret_val;
8158cc64f1eSJack F Vogel 
8168cc64f1eSJack F Vogel 	DEBUGFUNC("e1000_init_hw_i210");
8178cc64f1eSJack F Vogel 	if ((hw->mac.type >= e1000_i210) &&
8188cc64f1eSJack F Vogel 	    !(e1000_get_flash_presence_i210(hw))) {
8198cc64f1eSJack F Vogel 		ret_val = e1000_pll_workaround_i210(hw);
8208cc64f1eSJack F Vogel 		if (ret_val != E1000_SUCCESS)
8218cc64f1eSJack F Vogel 			return ret_val;
8228cc64f1eSJack F Vogel 	}
823c80429ceSEric Joyner 	hw->phy.ops.get_cfg_done = e1000_get_cfg_done_i210;
82451569bd7SEric Joyner 
82551569bd7SEric Joyner 	/* Initialize identification LED */
82651569bd7SEric Joyner 	mac->ops.id_led_init(hw);
82751569bd7SEric Joyner 
8286b9d35faSGuinan Sun 	ret_val = e1000_init_hw_base(hw);
8298cc64f1eSJack F Vogel 	return ret_val;
8308cc64f1eSJack F Vogel }
831