]>
Commit | Line | Data |
---|---|---|
56a62fc8 JB |
1 | /******************************************************************************* |
2 | * | |
3 | * Intel Ethernet Controller XL710 Family Linux Driver | |
dc641b73 | 4 | * Copyright(c) 2013 - 2014 Intel Corporation. |
56a62fc8 JB |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
dc641b73 GR |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program. If not, see <http://www.gnu.org/licenses/>. | |
56a62fc8 JB |
17 | * |
18 | * The full GNU General Public License is included in this distribution in | |
19 | * the file called "COPYING". | |
20 | * | |
21 | * Contact Information: | |
22 | * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> | |
23 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
24 | * | |
25 | ******************************************************************************/ | |
26 | ||
27 | #include "i40e_prototype.h" | |
28 | ||
29 | /** | |
3e26186d SN |
30 | * i40e_init_nvm_ops - Initialize NVM function pointers |
31 | * @hw: pointer to the HW structure | |
56a62fc8 | 32 | * |
3e26186d SN |
33 | * Setup the function pointers and the NVM info structure. Should be called |
34 | * once per NVM initialization, e.g. inside the i40e_init_shared_code(). | |
35 | * Please notice that the NVM term is used here (& in all methods covered | |
36 | * in this file) as an equivalent of the FLASH part mapped into the SR. | |
37 | * We are accessing FLASH always thru the Shadow RAM. | |
56a62fc8 JB |
38 | **/ |
39 | i40e_status i40e_init_nvm(struct i40e_hw *hw) | |
40 | { | |
41 | struct i40e_nvm_info *nvm = &hw->nvm; | |
42 | i40e_status ret_code = 0; | |
43 | u32 fla, gens; | |
44 | u8 sr_size; | |
45 | ||
46 | /* The SR size is stored regardless of the nvm programming mode | |
47 | * as the blank mode may be used in the factory line. | |
48 | */ | |
49 | gens = rd32(hw, I40E_GLNVM_GENS); | |
50 | sr_size = ((gens & I40E_GLNVM_GENS_SR_SIZE_MASK) >> | |
51 | I40E_GLNVM_GENS_SR_SIZE_SHIFT); | |
3e26186d | 52 | /* Switching to words (sr_size contains power of 2KB) */ |
56a62fc8 JB |
53 | nvm->sr_size = (1 << sr_size) * I40E_SR_WORDS_IN_1KB; |
54 | ||
3e26186d | 55 | /* Check if we are in the normal or blank NVM programming mode */ |
56a62fc8 | 56 | fla = rd32(hw, I40E_GLNVM_FLA); |
3e26186d SN |
57 | if (fla & I40E_GLNVM_FLA_LOCKED_MASK) { /* Normal programming mode */ |
58 | /* Max NVM timeout */ | |
56a62fc8 JB |
59 | nvm->timeout = I40E_MAX_NVM_TIMEOUT; |
60 | nvm->blank_nvm_mode = false; | |
3e26186d | 61 | } else { /* Blank programming mode */ |
56a62fc8 JB |
62 | nvm->blank_nvm_mode = true; |
63 | ret_code = I40E_ERR_NVM_BLANK_MODE; | |
74d0d0ed | 64 | i40e_debug(hw, I40E_DEBUG_NVM, "NVM init error: unsupported blank mode.\n"); |
56a62fc8 JB |
65 | } |
66 | ||
67 | return ret_code; | |
68 | } | |
69 | ||
70 | /** | |
3e26186d SN |
71 | * i40e_acquire_nvm - Generic request for acquiring the NVM ownership |
72 | * @hw: pointer to the HW structure | |
73 | * @access: NVM access type (read or write) | |
56a62fc8 | 74 | * |
3e26186d SN |
75 | * This function will request NVM ownership for reading |
76 | * via the proper Admin Command. | |
56a62fc8 JB |
77 | **/ |
78 | i40e_status i40e_acquire_nvm(struct i40e_hw *hw, | |
79 | enum i40e_aq_resource_access_type access) | |
80 | { | |
81 | i40e_status ret_code = 0; | |
82 | u64 gtime, timeout; | |
83 | u64 time = 0; | |
84 | ||
85 | if (hw->nvm.blank_nvm_mode) | |
86 | goto i40e_i40e_acquire_nvm_exit; | |
87 | ||
88 | ret_code = i40e_aq_request_resource(hw, I40E_NVM_RESOURCE_ID, access, | |
89 | 0, &time, NULL); | |
3e26186d | 90 | /* Reading the Global Device Timer */ |
56a62fc8 JB |
91 | gtime = rd32(hw, I40E_GLVFGEN_TIMER); |
92 | ||
3e26186d | 93 | /* Store the timeout */ |
56a62fc8 JB |
94 | hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time) + gtime; |
95 | ||
96 | if (ret_code) { | |
3e26186d | 97 | /* Set the polling timeout */ |
56a62fc8 JB |
98 | if (time > I40E_MAX_NVM_TIMEOUT) |
99 | timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT) | |
100 | + gtime; | |
101 | else | |
102 | timeout = hw->nvm.hw_semaphore_timeout; | |
3e26186d | 103 | /* Poll until the current NVM owner timeouts */ |
56a62fc8 JB |
104 | while (gtime < timeout) { |
105 | usleep_range(10000, 20000); | |
106 | ret_code = i40e_aq_request_resource(hw, | |
107 | I40E_NVM_RESOURCE_ID, | |
108 | access, 0, &time, | |
109 | NULL); | |
110 | if (!ret_code) { | |
111 | hw->nvm.hw_semaphore_timeout = | |
112 | I40E_MS_TO_GTIME(time) + gtime; | |
113 | break; | |
114 | } | |
115 | gtime = rd32(hw, I40E_GLVFGEN_TIMER); | |
116 | } | |
117 | if (ret_code) { | |
118 | hw->nvm.hw_semaphore_timeout = 0; | |
74d0d0ed SN |
119 | i40e_debug(hw, I40E_DEBUG_NVM, |
120 | "NVM acquire timed out, wait %llu ms before trying again.\n", | |
121 | time); | |
56a62fc8 JB |
122 | } |
123 | } | |
124 | ||
125 | i40e_i40e_acquire_nvm_exit: | |
126 | return ret_code; | |
127 | } | |
128 | ||
129 | /** | |
3e26186d SN |
130 | * i40e_release_nvm - Generic request for releasing the NVM ownership |
131 | * @hw: pointer to the HW structure | |
56a62fc8 | 132 | * |
3e26186d | 133 | * This function will release NVM resource via the proper Admin Command. |
56a62fc8 JB |
134 | **/ |
135 | void i40e_release_nvm(struct i40e_hw *hw) | |
136 | { | |
137 | if (!hw->nvm.blank_nvm_mode) | |
138 | i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL); | |
139 | } | |
140 | ||
141 | /** | |
3e26186d SN |
142 | * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit |
143 | * @hw: pointer to the HW structure | |
56a62fc8 | 144 | * |
3e26186d | 145 | * Polls the SRCTL Shadow RAM register done bit. |
56a62fc8 JB |
146 | **/ |
147 | static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw) | |
148 | { | |
149 | i40e_status ret_code = I40E_ERR_TIMEOUT; | |
150 | u32 srctl, wait_cnt; | |
151 | ||
3e26186d | 152 | /* Poll the I40E_GLNVM_SRCTL until the done bit is set */ |
56a62fc8 JB |
153 | for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) { |
154 | srctl = rd32(hw, I40E_GLNVM_SRCTL); | |
155 | if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) { | |
156 | ret_code = 0; | |
157 | break; | |
158 | } | |
159 | udelay(5); | |
160 | } | |
161 | if (ret_code == I40E_ERR_TIMEOUT) | |
74d0d0ed | 162 | i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set"); |
56a62fc8 JB |
163 | return ret_code; |
164 | } | |
165 | ||
166 | /** | |
3e26186d SN |
167 | * i40e_read_nvm_word - Reads Shadow RAM |
168 | * @hw: pointer to the HW structure | |
169 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) | |
170 | * @data: word read from the Shadow RAM | |
56a62fc8 | 171 | * |
3e26186d | 172 | * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. |
56a62fc8 | 173 | **/ |
a4bcfbb7 SN |
174 | i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, |
175 | u16 *data) | |
56a62fc8 JB |
176 | { |
177 | i40e_status ret_code = I40E_ERR_TIMEOUT; | |
178 | u32 sr_reg; | |
179 | ||
180 | if (offset >= hw->nvm.sr_size) { | |
74d0d0ed SN |
181 | i40e_debug(hw, I40E_DEBUG_NVM, |
182 | "NVM read error: offset %d beyond Shadow RAM limit %d\n", | |
183 | offset, hw->nvm.sr_size); | |
56a62fc8 JB |
184 | ret_code = I40E_ERR_PARAM; |
185 | goto read_nvm_exit; | |
186 | } | |
187 | ||
3e26186d | 188 | /* Poll the done bit first */ |
56a62fc8 JB |
189 | ret_code = i40e_poll_sr_srctl_done_bit(hw); |
190 | if (!ret_code) { | |
3e26186d | 191 | /* Write the address and start reading */ |
56a62fc8 JB |
192 | sr_reg = (u32)(offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) | |
193 | (1 << I40E_GLNVM_SRCTL_START_SHIFT); | |
194 | wr32(hw, I40E_GLNVM_SRCTL, sr_reg); | |
195 | ||
3e26186d | 196 | /* Poll I40E_GLNVM_SRCTL until the done bit is set */ |
56a62fc8 JB |
197 | ret_code = i40e_poll_sr_srctl_done_bit(hw); |
198 | if (!ret_code) { | |
199 | sr_reg = rd32(hw, I40E_GLNVM_SRDATA); | |
200 | *data = (u16)((sr_reg & | |
201 | I40E_GLNVM_SRDATA_RDDATA_MASK) | |
202 | >> I40E_GLNVM_SRDATA_RDDATA_SHIFT); | |
203 | } | |
204 | } | |
205 | if (ret_code) | |
74d0d0ed SN |
206 | i40e_debug(hw, I40E_DEBUG_NVM, |
207 | "NVM read error: Couldn't access Shadow RAM address: 0x%x\n", | |
208 | offset); | |
56a62fc8 JB |
209 | |
210 | read_nvm_exit: | |
211 | return ret_code; | |
212 | } | |
213 | ||
56a62fc8 | 214 | /** |
3e26186d SN |
215 | * i40e_read_nvm_buffer - Reads Shadow RAM buffer |
216 | * @hw: pointer to the HW structure | |
217 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). | |
218 | * @words: (in) number of words to read; (out) number of words actually read | |
219 | * @data: words read from the Shadow RAM | |
56a62fc8 | 220 | * |
3e26186d SN |
221 | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() |
222 | * method. The buffer read is preceded by the NVM ownership take | |
223 | * and followed by the release. | |
56a62fc8 JB |
224 | **/ |
225 | i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, | |
226 | u16 *words, u16 *data) | |
227 | { | |
228 | i40e_status ret_code = 0; | |
229 | u16 index, word; | |
56a62fc8 | 230 | |
3e26186d | 231 | /* Loop thru the selected region */ |
a4bcfbb7 SN |
232 | for (word = 0; word < *words; word++) { |
233 | index = offset + word; | |
234 | ret_code = i40e_read_nvm_word(hw, index, &data[word]); | |
235 | if (ret_code) | |
236 | break; | |
56a62fc8 JB |
237 | } |
238 | ||
3e26186d | 239 | /* Update the number of words read from the Shadow RAM */ |
a4bcfbb7 SN |
240 | *words = word; |
241 | ||
56a62fc8 JB |
242 | return ret_code; |
243 | } | |
244 | ||
cd552cb4 SN |
245 | /** |
246 | * i40e_write_nvm_aq - Writes Shadow RAM. | |
247 | * @hw: pointer to the HW structure. | |
248 | * @module_pointer: module pointer location in words from the NVM beginning | |
249 | * @offset: offset in words from module start | |
250 | * @words: number of words to write | |
251 | * @data: buffer with words to write to the Shadow RAM | |
252 | * @last_command: tells the AdminQ that this is the last command | |
253 | * | |
254 | * Writes a 16 bit words buffer to the Shadow RAM using the admin command. | |
255 | **/ | |
952d9639 WY |
256 | static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, |
257 | u32 offset, u16 words, void *data, | |
258 | bool last_command) | |
cd552cb4 SN |
259 | { |
260 | i40e_status ret_code = I40E_ERR_NVM; | |
261 | ||
262 | /* Here we are checking the SR limit only for the flat memory model. | |
263 | * We cannot do it for the module-based model, as we did not acquire | |
264 | * the NVM resource yet (we cannot get the module pointer value). | |
265 | * Firmware will check the module-based model. | |
266 | */ | |
267 | if ((offset + words) > hw->nvm.sr_size) | |
74d0d0ed SN |
268 | i40e_debug(hw, I40E_DEBUG_NVM, |
269 | "NVM write error: offset %d beyond Shadow RAM limit %d\n", | |
270 | (offset + words), hw->nvm.sr_size); | |
cd552cb4 SN |
271 | else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS) |
272 | /* We can write only up to 4KB (one sector), in one AQ write */ | |
74d0d0ed SN |
273 | i40e_debug(hw, I40E_DEBUG_NVM, |
274 | "NVM write fail error: tried to write %d words, limit is %d.\n", | |
275 | words, I40E_SR_SECTOR_SIZE_IN_WORDS); | |
cd552cb4 SN |
276 | else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS) |
277 | != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS)) | |
278 | /* A single write cannot spread over two sectors */ | |
74d0d0ed SN |
279 | i40e_debug(hw, I40E_DEBUG_NVM, |
280 | "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n", | |
281 | offset, words); | |
cd552cb4 SN |
282 | else |
283 | ret_code = i40e_aq_update_nvm(hw, module_pointer, | |
284 | 2 * offset, /*bytes*/ | |
285 | 2 * words, /*bytes*/ | |
286 | data, last_command, NULL); | |
287 | ||
288 | return ret_code; | |
289 | } | |
290 | ||
56a62fc8 | 291 | /** |
3e26186d SN |
292 | * i40e_calc_nvm_checksum - Calculates and returns the checksum |
293 | * @hw: pointer to hardware structure | |
294 | * @checksum: pointer to the checksum | |
56a62fc8 | 295 | * |
3e26186d SN |
296 | * This function calculates SW Checksum that covers the whole 64kB shadow RAM |
297 | * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD | |
298 | * is customer specific and unknown. Therefore, this function skips all maximum | |
299 | * possible size of VPD (1kB). | |
56a62fc8 JB |
300 | **/ |
301 | static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, | |
302 | u16 *checksum) | |
303 | { | |
304 | i40e_status ret_code = 0; | |
305 | u16 pcie_alt_module = 0; | |
306 | u16 checksum_local = 0; | |
307 | u16 vpd_module = 0; | |
308 | u16 word = 0; | |
309 | u32 i = 0; | |
310 | ||
311 | /* read pointer to VPD area */ | |
a4bcfbb7 | 312 | ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); |
56a62fc8 JB |
313 | if (ret_code) { |
314 | ret_code = I40E_ERR_NVM_CHECKSUM; | |
315 | goto i40e_calc_nvm_checksum_exit; | |
316 | } | |
317 | ||
318 | /* read pointer to PCIe Alt Auto-load module */ | |
a4bcfbb7 | 319 | ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, |
56a62fc8 JB |
320 | &pcie_alt_module); |
321 | if (ret_code) { | |
322 | ret_code = I40E_ERR_NVM_CHECKSUM; | |
323 | goto i40e_calc_nvm_checksum_exit; | |
324 | } | |
325 | ||
326 | /* Calculate SW checksum that covers the whole 64kB shadow RAM | |
327 | * except the VPD and PCIe ALT Auto-load modules | |
328 | */ | |
329 | for (i = 0; i < hw->nvm.sr_size; i++) { | |
330 | /* Skip Checksum word */ | |
331 | if (i == I40E_SR_SW_CHECKSUM_WORD) | |
332 | i++; | |
333 | /* Skip VPD module (convert byte size to word count) */ | |
334 | if (i == (u32)vpd_module) { | |
335 | i += (I40E_SR_VPD_MODULE_MAX_SIZE / 2); | |
336 | if (i >= hw->nvm.sr_size) | |
337 | break; | |
338 | } | |
339 | /* Skip PCIe ALT module (convert byte size to word count) */ | |
340 | if (i == (u32)pcie_alt_module) { | |
341 | i += (I40E_SR_PCIE_ALT_MODULE_MAX_SIZE / 2); | |
342 | if (i >= hw->nvm.sr_size) | |
343 | break; | |
344 | } | |
345 | ||
a4bcfbb7 | 346 | ret_code = i40e_read_nvm_word(hw, (u16)i, &word); |
56a62fc8 JB |
347 | if (ret_code) { |
348 | ret_code = I40E_ERR_NVM_CHECKSUM; | |
349 | goto i40e_calc_nvm_checksum_exit; | |
350 | } | |
351 | checksum_local += word; | |
352 | } | |
353 | ||
354 | *checksum = (u16)I40E_SR_SW_CHECKSUM_BASE - checksum_local; | |
355 | ||
356 | i40e_calc_nvm_checksum_exit: | |
357 | return ret_code; | |
358 | } | |
359 | ||
cd552cb4 SN |
360 | /** |
361 | * i40e_update_nvm_checksum - Updates the NVM checksum | |
362 | * @hw: pointer to hardware structure | |
363 | * | |
364 | * NVM ownership must be acquired before calling this function and released | |
365 | * on ARQ completion event reception by caller. | |
366 | * This function will commit SR to NVM. | |
367 | **/ | |
368 | i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) | |
369 | { | |
370 | i40e_status ret_code = 0; | |
371 | u16 checksum; | |
372 | ||
373 | ret_code = i40e_calc_nvm_checksum(hw, &checksum); | |
374 | if (!ret_code) | |
375 | ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, | |
376 | 1, &checksum, true); | |
377 | ||
378 | return ret_code; | |
379 | } | |
380 | ||
56a62fc8 | 381 | /** |
3e26186d SN |
382 | * i40e_validate_nvm_checksum - Validate EEPROM checksum |
383 | * @hw: pointer to hardware structure | |
384 | * @checksum: calculated checksum | |
56a62fc8 | 385 | * |
3e26186d SN |
386 | * Performs checksum calculation and validates the NVM SW checksum. If the |
387 | * caller does not need checksum, the value can be NULL. | |
56a62fc8 JB |
388 | **/ |
389 | i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, | |
390 | u16 *checksum) | |
391 | { | |
392 | i40e_status ret_code = 0; | |
393 | u16 checksum_sr = 0; | |
e15c9fa0 | 394 | u16 checksum_local = 0; |
56a62fc8 | 395 | |
56a62fc8 JB |
396 | ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); |
397 | if (ret_code) | |
7a208e83 | 398 | goto i40e_validate_nvm_checksum_exit; |
56a62fc8 JB |
399 | |
400 | /* Do not use i40e_read_nvm_word() because we do not want to take | |
401 | * the synchronization semaphores twice here. | |
402 | */ | |
a4bcfbb7 | 403 | i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); |
56a62fc8 JB |
404 | |
405 | /* Verify read checksum from EEPROM is the same as | |
406 | * calculated checksum | |
407 | */ | |
408 | if (checksum_local != checksum_sr) | |
409 | ret_code = I40E_ERR_NVM_CHECKSUM; | |
410 | ||
411 | /* If the user cares, return the calculated checksum */ | |
412 | if (checksum) | |
413 | *checksum = checksum_local; | |
414 | ||
56a62fc8 JB |
415 | i40e_validate_nvm_checksum_exit: |
416 | return ret_code; | |
417 | } | |
cd552cb4 SN |
418 | |
419 | static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, | |
420 | struct i40e_nvm_access *cmd, | |
421 | u8 *bytes, int *errno); | |
422 | static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, | |
423 | struct i40e_nvm_access *cmd, | |
424 | u8 *bytes, int *errno); | |
425 | static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, | |
426 | struct i40e_nvm_access *cmd, | |
427 | u8 *bytes, int *errno); | |
428 | static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, | |
429 | struct i40e_nvm_access *cmd, | |
430 | int *errno); | |
431 | static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, | |
432 | struct i40e_nvm_access *cmd, | |
433 | int *errno); | |
434 | static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, | |
435 | struct i40e_nvm_access *cmd, | |
436 | u8 *bytes, int *errno); | |
437 | static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, | |
438 | struct i40e_nvm_access *cmd, | |
439 | u8 *bytes, int *errno); | |
440 | static inline u8 i40e_nvmupd_get_module(u32 val) | |
441 | { | |
442 | return (u8)(val & I40E_NVM_MOD_PNT_MASK); | |
443 | } | |
444 | static inline u8 i40e_nvmupd_get_transaction(u32 val) | |
445 | { | |
446 | return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); | |
447 | } | |
448 | ||
74d0d0ed SN |
449 | static char *i40e_nvm_update_state_str[] = { |
450 | "I40E_NVMUPD_INVALID", | |
451 | "I40E_NVMUPD_READ_CON", | |
452 | "I40E_NVMUPD_READ_SNT", | |
453 | "I40E_NVMUPD_READ_LCB", | |
454 | "I40E_NVMUPD_READ_SA", | |
455 | "I40E_NVMUPD_WRITE_ERA", | |
456 | "I40E_NVMUPD_WRITE_CON", | |
457 | "I40E_NVMUPD_WRITE_SNT", | |
458 | "I40E_NVMUPD_WRITE_LCB", | |
459 | "I40E_NVMUPD_WRITE_SA", | |
460 | "I40E_NVMUPD_CSUM_CON", | |
461 | "I40E_NVMUPD_CSUM_SA", | |
462 | "I40E_NVMUPD_CSUM_LCB", | |
463 | }; | |
464 | ||
cd552cb4 SN |
465 | /** |
466 | * i40e_nvmupd_command - Process an NVM update command | |
467 | * @hw: pointer to hardware structure | |
468 | * @cmd: pointer to nvm update command | |
469 | * @bytes: pointer to the data buffer | |
470 | * @errno: pointer to return error code | |
471 | * | |
472 | * Dispatches command depending on what update state is current | |
473 | **/ | |
474 | i40e_status i40e_nvmupd_command(struct i40e_hw *hw, | |
475 | struct i40e_nvm_access *cmd, | |
476 | u8 *bytes, int *errno) | |
477 | { | |
478 | i40e_status status; | |
479 | ||
480 | /* assume success */ | |
481 | *errno = 0; | |
482 | ||
483 | switch (hw->nvmupd_state) { | |
484 | case I40E_NVMUPD_STATE_INIT: | |
485 | status = i40e_nvmupd_state_init(hw, cmd, bytes, errno); | |
486 | break; | |
487 | ||
488 | case I40E_NVMUPD_STATE_READING: | |
489 | status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno); | |
490 | break; | |
491 | ||
492 | case I40E_NVMUPD_STATE_WRITING: | |
493 | status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno); | |
494 | break; | |
495 | ||
496 | default: | |
497 | /* invalid state, should never happen */ | |
74d0d0ed SN |
498 | i40e_debug(hw, I40E_DEBUG_NVM, |
499 | "NVMUPD: no such state %d\n", hw->nvmupd_state); | |
cd552cb4 SN |
500 | status = I40E_NOT_SUPPORTED; |
501 | *errno = -ESRCH; | |
502 | break; | |
503 | } | |
504 | return status; | |
505 | } | |
506 | ||
507 | /** | |
508 | * i40e_nvmupd_state_init - Handle NVM update state Init | |
509 | * @hw: pointer to hardware structure | |
510 | * @cmd: pointer to nvm update command buffer | |
511 | * @bytes: pointer to the data buffer | |
512 | * @errno: pointer to return error code | |
513 | * | |
514 | * Process legitimate commands of the Init state and conditionally set next | |
515 | * state. Reject all other commands. | |
516 | **/ | |
517 | static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, | |
518 | struct i40e_nvm_access *cmd, | |
519 | u8 *bytes, int *errno) | |
520 | { | |
521 | i40e_status status = 0; | |
522 | enum i40e_nvmupd_cmd upd_cmd; | |
523 | ||
524 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); | |
525 | ||
526 | switch (upd_cmd) { | |
527 | case I40E_NVMUPD_READ_SA: | |
528 | status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); | |
529 | if (status) { | |
530 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); | |
531 | } else { | |
532 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); | |
533 | i40e_release_nvm(hw); | |
534 | } | |
535 | break; | |
536 | ||
537 | case I40E_NVMUPD_READ_SNT: | |
538 | status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); | |
539 | if (status) { | |
540 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); | |
541 | } else { | |
542 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); | |
543 | hw->nvmupd_state = I40E_NVMUPD_STATE_READING; | |
544 | } | |
545 | break; | |
546 | ||
547 | case I40E_NVMUPD_WRITE_ERA: | |
548 | status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |
549 | if (status) { | |
550 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); | |
551 | } else { | |
552 | status = i40e_nvmupd_nvm_erase(hw, cmd, errno); | |
553 | if (status) | |
554 | i40e_release_nvm(hw); | |
555 | else | |
556 | hw->aq.nvm_release_on_done = true; | |
557 | } | |
558 | break; | |
559 | ||
560 | case I40E_NVMUPD_WRITE_SA: | |
561 | status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |
562 | if (status) { | |
563 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); | |
564 | } else { | |
565 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); | |
566 | if (status) | |
567 | i40e_release_nvm(hw); | |
568 | else | |
569 | hw->aq.nvm_release_on_done = true; | |
570 | } | |
571 | break; | |
572 | ||
573 | case I40E_NVMUPD_WRITE_SNT: | |
574 | status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |
575 | if (status) { | |
576 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); | |
577 | } else { | |
578 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); | |
579 | hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; | |
580 | } | |
581 | break; | |
582 | ||
583 | case I40E_NVMUPD_CSUM_SA: | |
584 | status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |
585 | if (status) { | |
586 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); | |
587 | } else { | |
588 | status = i40e_update_nvm_checksum(hw); | |
589 | if (status) { | |
590 | *errno = hw->aq.asq_last_status ? | |
591 | i40e_aq_rc_to_posix(hw->aq.asq_last_status) : | |
592 | -EIO; | |
593 | i40e_release_nvm(hw); | |
594 | } else { | |
595 | hw->aq.nvm_release_on_done = true; | |
596 | } | |
597 | } | |
598 | break; | |
599 | ||
600 | default: | |
74d0d0ed SN |
601 | i40e_debug(hw, I40E_DEBUG_NVM, |
602 | "NVMUPD: bad cmd %s in init state\n", | |
603 | i40e_nvm_update_state_str[upd_cmd]); | |
cd552cb4 SN |
604 | status = I40E_ERR_NVM; |
605 | *errno = -ESRCH; | |
606 | break; | |
607 | } | |
608 | return status; | |
609 | } | |
610 | ||
611 | /** | |
612 | * i40e_nvmupd_state_reading - Handle NVM update state Reading | |
613 | * @hw: pointer to hardware structure | |
614 | * @cmd: pointer to nvm update command buffer | |
615 | * @bytes: pointer to the data buffer | |
616 | * @errno: pointer to return error code | |
617 | * | |
618 | * NVM ownership is already held. Process legitimate commands and set any | |
619 | * change in state; reject all other commands. | |
620 | **/ | |
621 | static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, | |
622 | struct i40e_nvm_access *cmd, | |
623 | u8 *bytes, int *errno) | |
624 | { | |
625 | i40e_status status; | |
626 | enum i40e_nvmupd_cmd upd_cmd; | |
627 | ||
628 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); | |
629 | ||
630 | switch (upd_cmd) { | |
631 | case I40E_NVMUPD_READ_SA: | |
632 | case I40E_NVMUPD_READ_CON: | |
633 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); | |
634 | break; | |
635 | ||
636 | case I40E_NVMUPD_READ_LCB: | |
637 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); | |
638 | i40e_release_nvm(hw); | |
639 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |
640 | break; | |
641 | ||
642 | default: | |
74d0d0ed SN |
643 | i40e_debug(hw, I40E_DEBUG_NVM, |
644 | "NVMUPD: bad cmd %s in reading state.\n", | |
645 | i40e_nvm_update_state_str[upd_cmd]); | |
cd552cb4 SN |
646 | status = I40E_NOT_SUPPORTED; |
647 | *errno = -ESRCH; | |
648 | break; | |
649 | } | |
650 | return status; | |
651 | } | |
652 | ||
653 | /** | |
654 | * i40e_nvmupd_state_writing - Handle NVM update state Writing | |
655 | * @hw: pointer to hardware structure | |
656 | * @cmd: pointer to nvm update command buffer | |
657 | * @bytes: pointer to the data buffer | |
658 | * @errno: pointer to return error code | |
659 | * | |
660 | * NVM ownership is already held. Process legitimate commands and set any | |
661 | * change in state; reject all other commands | |
662 | **/ | |
663 | static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, | |
664 | struct i40e_nvm_access *cmd, | |
665 | u8 *bytes, int *errno) | |
666 | { | |
667 | i40e_status status; | |
668 | enum i40e_nvmupd_cmd upd_cmd; | |
669 | ||
670 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); | |
671 | ||
672 | switch (upd_cmd) { | |
673 | case I40E_NVMUPD_WRITE_CON: | |
674 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); | |
675 | break; | |
676 | ||
677 | case I40E_NVMUPD_WRITE_LCB: | |
678 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); | |
679 | if (!status) { | |
680 | hw->aq.nvm_release_on_done = true; | |
681 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |
682 | } | |
683 | break; | |
684 | ||
685 | case I40E_NVMUPD_CSUM_CON: | |
686 | status = i40e_update_nvm_checksum(hw); | |
687 | if (status) | |
688 | *errno = hw->aq.asq_last_status ? | |
689 | i40e_aq_rc_to_posix(hw->aq.asq_last_status) : | |
690 | -EIO; | |
691 | break; | |
692 | ||
693 | case I40E_NVMUPD_CSUM_LCB: | |
694 | status = i40e_update_nvm_checksum(hw); | |
695 | if (status) { | |
696 | *errno = hw->aq.asq_last_status ? | |
697 | i40e_aq_rc_to_posix(hw->aq.asq_last_status) : | |
698 | -EIO; | |
699 | } else { | |
700 | hw->aq.nvm_release_on_done = true; | |
701 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |
702 | } | |
703 | break; | |
704 | ||
705 | default: | |
74d0d0ed SN |
706 | i40e_debug(hw, I40E_DEBUG_NVM, |
707 | "NVMUPD: bad cmd %s in writing state.\n", | |
708 | i40e_nvm_update_state_str[upd_cmd]); | |
cd552cb4 SN |
709 | status = I40E_NOT_SUPPORTED; |
710 | *errno = -ESRCH; | |
711 | break; | |
712 | } | |
713 | return status; | |
714 | } | |
715 | ||
716 | /** | |
717 | * i40e_nvmupd_validate_command - Validate given command | |
718 | * @hw: pointer to hardware structure | |
719 | * @cmd: pointer to nvm update command buffer | |
720 | * @errno: pointer to return error code | |
721 | * | |
722 | * Return one of the valid command types or I40E_NVMUPD_INVALID | |
723 | **/ | |
724 | static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, | |
725 | struct i40e_nvm_access *cmd, | |
726 | int *errno) | |
727 | { | |
728 | enum i40e_nvmupd_cmd upd_cmd; | |
729 | u8 transaction, module; | |
730 | ||
731 | /* anything that doesn't match a recognized case is an error */ | |
732 | upd_cmd = I40E_NVMUPD_INVALID; | |
733 | ||
734 | transaction = i40e_nvmupd_get_transaction(cmd->config); | |
735 | module = i40e_nvmupd_get_module(cmd->config); | |
736 | ||
737 | /* limits on data size */ | |
738 | if ((cmd->data_size < 1) || | |
739 | (cmd->data_size > I40E_NVMUPD_MAX_DATA)) { | |
74d0d0ed SN |
740 | i40e_debug(hw, I40E_DEBUG_NVM, |
741 | "i40e_nvmupd_validate_command data_size %d\n", | |
742 | cmd->data_size); | |
cd552cb4 SN |
743 | *errno = -EFAULT; |
744 | return I40E_NVMUPD_INVALID; | |
745 | } | |
746 | ||
747 | switch (cmd->command) { | |
748 | case I40E_NVM_READ: | |
749 | switch (transaction) { | |
750 | case I40E_NVM_CON: | |
751 | upd_cmd = I40E_NVMUPD_READ_CON; | |
752 | break; | |
753 | case I40E_NVM_SNT: | |
754 | upd_cmd = I40E_NVMUPD_READ_SNT; | |
755 | break; | |
756 | case I40E_NVM_LCB: | |
757 | upd_cmd = I40E_NVMUPD_READ_LCB; | |
758 | break; | |
759 | case I40E_NVM_SA: | |
760 | upd_cmd = I40E_NVMUPD_READ_SA; | |
761 | break; | |
762 | } | |
763 | break; | |
764 | ||
765 | case I40E_NVM_WRITE: | |
766 | switch (transaction) { | |
767 | case I40E_NVM_CON: | |
768 | upd_cmd = I40E_NVMUPD_WRITE_CON; | |
769 | break; | |
770 | case I40E_NVM_SNT: | |
771 | upd_cmd = I40E_NVMUPD_WRITE_SNT; | |
772 | break; | |
773 | case I40E_NVM_LCB: | |
774 | upd_cmd = I40E_NVMUPD_WRITE_LCB; | |
775 | break; | |
776 | case I40E_NVM_SA: | |
777 | upd_cmd = I40E_NVMUPD_WRITE_SA; | |
778 | break; | |
779 | case I40E_NVM_ERA: | |
780 | upd_cmd = I40E_NVMUPD_WRITE_ERA; | |
781 | break; | |
782 | case I40E_NVM_CSUM: | |
783 | upd_cmd = I40E_NVMUPD_CSUM_CON; | |
784 | break; | |
785 | case (I40E_NVM_CSUM|I40E_NVM_SA): | |
786 | upd_cmd = I40E_NVMUPD_CSUM_SA; | |
787 | break; | |
788 | case (I40E_NVM_CSUM|I40E_NVM_LCB): | |
789 | upd_cmd = I40E_NVMUPD_CSUM_LCB; | |
790 | break; | |
791 | } | |
792 | break; | |
793 | } | |
74d0d0ed SN |
794 | i40e_debug(hw, I40E_DEBUG_NVM, "%s\n", |
795 | i40e_nvm_update_state_str[upd_cmd]); | |
cd552cb4 SN |
796 | |
797 | if (upd_cmd == I40E_NVMUPD_INVALID) { | |
798 | *errno = -EFAULT; | |
74d0d0ed SN |
799 | i40e_debug(hw, I40E_DEBUG_NVM, |
800 | "i40e_nvmupd_validate_command returns %d errno %d\n", | |
801 | upd_cmd, *errno); | |
cd552cb4 SN |
802 | } |
803 | return upd_cmd; | |
804 | } | |
805 | ||
806 | /** | |
807 | * i40e_nvmupd_nvm_read - Read NVM | |
808 | * @hw: pointer to hardware structure | |
809 | * @cmd: pointer to nvm update command buffer | |
810 | * @bytes: pointer to the data buffer | |
811 | * @errno: pointer to return error code | |
812 | * | |
813 | * cmd structure contains identifiers and data buffer | |
814 | **/ | |
815 | static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, | |
816 | struct i40e_nvm_access *cmd, | |
817 | u8 *bytes, int *errno) | |
818 | { | |
819 | i40e_status status; | |
820 | u8 module, transaction; | |
821 | bool last; | |
822 | ||
823 | transaction = i40e_nvmupd_get_transaction(cmd->config); | |
824 | module = i40e_nvmupd_get_module(cmd->config); | |
825 | last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); | |
cd552cb4 SN |
826 | |
827 | status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, | |
828 | bytes, last, NULL); | |
74d0d0ed SN |
829 | if (status) { |
830 | i40e_debug(hw, I40E_DEBUG_NVM, | |
831 | "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", | |
832 | module, cmd->offset, cmd->data_size); | |
833 | i40e_debug(hw, I40E_DEBUG_NVM, | |
834 | "i40e_nvmupd_nvm_read status %d aq %d\n", | |
835 | status, hw->aq.asq_last_status); | |
cd552cb4 | 836 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); |
74d0d0ed | 837 | } |
cd552cb4 SN |
838 | |
839 | return status; | |
840 | } | |
841 | ||
842 | /** | |
843 | * i40e_nvmupd_nvm_erase - Erase an NVM module | |
844 | * @hw: pointer to hardware structure | |
845 | * @cmd: pointer to nvm update command buffer | |
846 | * @errno: pointer to return error code | |
847 | * | |
848 | * module, offset, data_size and data are in cmd structure | |
849 | **/ | |
850 | static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, | |
851 | struct i40e_nvm_access *cmd, | |
852 | int *errno) | |
853 | { | |
854 | i40e_status status = 0; | |
855 | u8 module, transaction; | |
856 | bool last; | |
857 | ||
858 | transaction = i40e_nvmupd_get_transaction(cmd->config); | |
859 | module = i40e_nvmupd_get_module(cmd->config); | |
860 | last = (transaction & I40E_NVM_LCB); | |
cd552cb4 SN |
861 | status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, |
862 | last, NULL); | |
74d0d0ed SN |
863 | if (status) { |
864 | i40e_debug(hw, I40E_DEBUG_NVM, | |
865 | "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", | |
866 | module, cmd->offset, cmd->data_size); | |
867 | i40e_debug(hw, I40E_DEBUG_NVM, | |
868 | "i40e_nvmupd_nvm_erase status %d aq %d\n", | |
869 | status, hw->aq.asq_last_status); | |
cd552cb4 | 870 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); |
74d0d0ed | 871 | } |
cd552cb4 SN |
872 | |
873 | return status; | |
874 | } | |
875 | ||
876 | /** | |
877 | * i40e_nvmupd_nvm_write - Write NVM | |
878 | * @hw: pointer to hardware structure | |
879 | * @cmd: pointer to nvm update command buffer | |
880 | * @bytes: pointer to the data buffer | |
881 | * @errno: pointer to return error code | |
882 | * | |
883 | * module, offset, data_size and data are in cmd structure | |
884 | **/ | |
885 | static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, | |
886 | struct i40e_nvm_access *cmd, | |
887 | u8 *bytes, int *errno) | |
888 | { | |
889 | i40e_status status = 0; | |
890 | u8 module, transaction; | |
891 | bool last; | |
892 | ||
893 | transaction = i40e_nvmupd_get_transaction(cmd->config); | |
894 | module = i40e_nvmupd_get_module(cmd->config); | |
895 | last = (transaction & I40E_NVM_LCB); | |
74d0d0ed | 896 | |
cd552cb4 SN |
897 | status = i40e_aq_update_nvm(hw, module, cmd->offset, |
898 | (u16)cmd->data_size, bytes, last, NULL); | |
74d0d0ed SN |
899 | if (status) { |
900 | i40e_debug(hw, I40E_DEBUG_NVM, | |
901 | "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", | |
902 | module, cmd->offset, cmd->data_size); | |
903 | i40e_debug(hw, I40E_DEBUG_NVM, | |
904 | "i40e_nvmupd_nvm_write status %d aq %d\n", | |
905 | status, hw->aq.asq_last_status); | |
cd552cb4 | 906 | *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status); |
74d0d0ed | 907 | } |
cd552cb4 SN |
908 | |
909 | return status; | |
910 | } |