1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2013 - 2018 Intel Corporation. */ |
3 | |
4 | #include <linux/bitfield.h> |
5 | #include <linux/delay.h> |
6 | #include "i40e_alloc.h" |
7 | #include "i40e_prototype.h" |
8 | |
9 | /** |
10 | * i40e_init_nvm - Initialize NVM function pointers |
11 | * @hw: pointer to the HW structure |
12 | * |
13 | * Setup the function pointers and the NVM info structure. Should be called |
14 | * once per NVM initialization, e.g. inside the i40e_init_shared_code(). |
15 | * Please notice that the NVM term is used here (& in all methods covered |
16 | * in this file) as an equivalent of the FLASH part mapped into the SR. |
17 | * We are accessing FLASH always thru the Shadow RAM. |
18 | **/ |
19 | int i40e_init_nvm(struct i40e_hw *hw) |
20 | { |
21 | struct i40e_nvm_info *nvm = &hw->nvm; |
22 | int ret_code = 0; |
23 | u32 fla, gens; |
24 | u8 sr_size; |
25 | |
26 | /* The SR size is stored regardless of the nvm programming mode |
27 | * as the blank mode may be used in the factory line. |
28 | */ |
29 | gens = rd32(hw, I40E_GLNVM_GENS); |
30 | sr_size = FIELD_GET(I40E_GLNVM_GENS_SR_SIZE_MASK, gens); |
31 | /* Switching to words (sr_size contains power of 2KB) */ |
32 | nvm->sr_size = BIT(sr_size) * I40E_SR_WORDS_IN_1KB; |
33 | |
34 | /* Check if we are in the normal or blank NVM programming mode */ |
35 | fla = rd32(hw, I40E_GLNVM_FLA); |
36 | if (fla & I40E_GLNVM_FLA_LOCKED_MASK) { /* Normal programming mode */ |
37 | /* Max NVM timeout */ |
38 | nvm->timeout = I40E_MAX_NVM_TIMEOUT; |
39 | nvm->blank_nvm_mode = false; |
40 | } else { /* Blank programming mode */ |
41 | nvm->blank_nvm_mode = true; |
42 | ret_code = -EIO; |
43 | i40e_debug(hw, I40E_DEBUG_NVM, "NVM init error: unsupported blank mode.\n" ); |
44 | } |
45 | |
46 | return ret_code; |
47 | } |
48 | |
49 | /** |
50 | * i40e_acquire_nvm - Generic request for acquiring the NVM ownership |
51 | * @hw: pointer to the HW structure |
52 | * @access: NVM access type (read or write) |
53 | * |
54 | * This function will request NVM ownership for reading |
55 | * via the proper Admin Command. |
56 | **/ |
57 | int i40e_acquire_nvm(struct i40e_hw *hw, |
58 | enum i40e_aq_resource_access_type access) |
59 | { |
60 | u64 gtime, timeout; |
61 | u64 time_left = 0; |
62 | int ret_code = 0; |
63 | |
64 | if (hw->nvm.blank_nvm_mode) |
65 | goto i40e_i40e_acquire_nvm_exit; |
66 | |
67 | ret_code = i40e_aq_request_resource(hw, resource: I40E_NVM_RESOURCE_ID, access, |
68 | sdp_number: 0, timeout: &time_left, NULL); |
69 | /* Reading the Global Device Timer */ |
70 | gtime = rd32(hw, I40E_GLVFGEN_TIMER); |
71 | |
72 | /* Store the timeout */ |
73 | hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time_left) + gtime; |
74 | |
75 | if (ret_code) |
76 | i40e_debug(hw, I40E_DEBUG_NVM, |
77 | "NVM acquire type %d failed time_left=%llu ret=%d aq_err=%d\n" , |
78 | access, time_left, ret_code, hw->aq.asq_last_status); |
79 | |
80 | if (ret_code && time_left) { |
81 | /* Poll until the current NVM owner timeouts */ |
82 | timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT) + gtime; |
83 | while ((gtime < timeout) && time_left) { |
84 | usleep_range(min: 10000, max: 20000); |
85 | gtime = rd32(hw, I40E_GLVFGEN_TIMER); |
86 | ret_code = i40e_aq_request_resource(hw, |
87 | resource: I40E_NVM_RESOURCE_ID, |
88 | access, sdp_number: 0, timeout: &time_left, |
89 | NULL); |
90 | if (!ret_code) { |
91 | hw->nvm.hw_semaphore_timeout = |
92 | I40E_MS_TO_GTIME(time_left) + gtime; |
93 | break; |
94 | } |
95 | } |
96 | if (ret_code) { |
97 | hw->nvm.hw_semaphore_timeout = 0; |
98 | i40e_debug(hw, I40E_DEBUG_NVM, |
99 | "NVM acquire timed out, wait %llu ms before trying again. status=%d aq_err=%d\n" , |
100 | time_left, ret_code, hw->aq.asq_last_status); |
101 | } |
102 | } |
103 | |
104 | i40e_i40e_acquire_nvm_exit: |
105 | return ret_code; |
106 | } |
107 | |
108 | /** |
109 | * i40e_release_nvm - Generic request for releasing the NVM ownership |
110 | * @hw: pointer to the HW structure |
111 | * |
112 | * This function will release NVM resource via the proper Admin Command. |
113 | **/ |
114 | void i40e_release_nvm(struct i40e_hw *hw) |
115 | { |
116 | u32 total_delay = 0; |
117 | int ret_code = 0; |
118 | |
119 | if (hw->nvm.blank_nvm_mode) |
120 | return; |
121 | |
122 | ret_code = i40e_aq_release_resource(hw, resource: I40E_NVM_RESOURCE_ID, sdp_number: 0, NULL); |
123 | |
124 | /* there are some rare cases when trying to release the resource |
125 | * results in an admin Q timeout, so handle them correctly |
126 | */ |
127 | while ((ret_code == -EIO) && |
128 | (total_delay < hw->aq.asq_cmd_timeout)) { |
129 | usleep_range(min: 1000, max: 2000); |
130 | ret_code = i40e_aq_release_resource(hw, |
131 | resource: I40E_NVM_RESOURCE_ID, |
132 | sdp_number: 0, NULL); |
133 | total_delay++; |
134 | } |
135 | } |
136 | |
137 | /** |
138 | * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit |
139 | * @hw: pointer to the HW structure |
140 | * |
141 | * Polls the SRCTL Shadow RAM register done bit. |
142 | **/ |
143 | static int i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw) |
144 | { |
145 | int ret_code = -EIO; |
146 | u32 srctl, wait_cnt; |
147 | |
148 | /* Poll the I40E_GLNVM_SRCTL until the done bit is set */ |
149 | for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) { |
150 | srctl = rd32(hw, I40E_GLNVM_SRCTL); |
151 | if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) { |
152 | ret_code = 0; |
153 | break; |
154 | } |
155 | udelay(5); |
156 | } |
157 | if (ret_code == -EIO) |
158 | i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set" ); |
159 | return ret_code; |
160 | } |
161 | |
162 | /** |
163 | * i40e_read_nvm_word_srctl - Reads Shadow RAM via SRCTL register |
164 | * @hw: pointer to the HW structure |
165 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) |
166 | * @data: word read from the Shadow RAM |
167 | * |
168 | * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. |
169 | **/ |
170 | static int i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, |
171 | u16 *data) |
172 | { |
173 | int ret_code = -EIO; |
174 | u32 sr_reg; |
175 | |
176 | if (offset >= hw->nvm.sr_size) { |
177 | i40e_debug(hw, I40E_DEBUG_NVM, |
178 | "NVM read error: offset %d beyond Shadow RAM limit %d\n" , |
179 | offset, hw->nvm.sr_size); |
180 | ret_code = -EINVAL; |
181 | goto read_nvm_exit; |
182 | } |
183 | |
184 | /* Poll the done bit first */ |
185 | ret_code = i40e_poll_sr_srctl_done_bit(hw); |
186 | if (!ret_code) { |
187 | /* Write the address and start reading */ |
188 | sr_reg = ((u32)offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) | |
189 | BIT(I40E_GLNVM_SRCTL_START_SHIFT); |
190 | wr32(hw, I40E_GLNVM_SRCTL, sr_reg); |
191 | |
192 | /* Poll I40E_GLNVM_SRCTL until the done bit is set */ |
193 | ret_code = i40e_poll_sr_srctl_done_bit(hw); |
194 | if (!ret_code) { |
195 | sr_reg = rd32(hw, I40E_GLNVM_SRDATA); |
196 | *data = FIELD_GET(I40E_GLNVM_SRDATA_RDDATA_MASK, |
197 | sr_reg); |
198 | } |
199 | } |
200 | if (ret_code) |
201 | i40e_debug(hw, I40E_DEBUG_NVM, |
202 | "NVM read error: Couldn't access Shadow RAM address: 0x%x\n" , |
203 | offset); |
204 | |
205 | read_nvm_exit: |
206 | return ret_code; |
207 | } |
208 | |
209 | /** |
210 | * i40e_read_nvm_aq - Read Shadow RAM. |
211 | * @hw: pointer to the HW structure. |
212 | * @module_pointer: module pointer location in words from the NVM beginning |
213 | * @offset: offset in words from module start |
214 | * @words: number of words to read |
215 | * @data: buffer with words to read to the Shadow RAM |
216 | * @last_command: tells the AdminQ that this is the last command |
217 | * |
218 | * Reads a 16 bit words buffer to the Shadow RAM using the admin command. |
219 | **/ |
220 | static int i40e_read_nvm_aq(struct i40e_hw *hw, |
221 | u8 module_pointer, u32 offset, |
222 | u16 words, void *data, |
223 | bool last_command) |
224 | { |
225 | struct i40e_asq_cmd_details cmd_details; |
226 | int ret_code = -EIO; |
227 | |
228 | memset(&cmd_details, 0, sizeof(cmd_details)); |
229 | cmd_details.wb_desc = &hw->nvm_wb_desc; |
230 | |
231 | /* Here we are checking the SR limit only for the flat memory model. |
232 | * We cannot do it for the module-based model, as we did not acquire |
233 | * the NVM resource yet (we cannot get the module pointer value). |
234 | * Firmware will check the module-based model. |
235 | */ |
236 | if ((offset + words) > hw->nvm.sr_size) |
237 | i40e_debug(hw, I40E_DEBUG_NVM, |
238 | "NVM read error: offset %d beyond Shadow RAM limit %d\n" , |
239 | (offset + words), hw->nvm.sr_size); |
240 | else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS) |
241 | /* We can read only up to 4KB (one sector), in one AQ write */ |
242 | i40e_debug(hw, I40E_DEBUG_NVM, |
243 | "NVM read fail error: tried to read %d words, limit is %d.\n" , |
244 | words, I40E_SR_SECTOR_SIZE_IN_WORDS); |
245 | else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS) |
246 | != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS)) |
247 | /* A single read cannot spread over two sectors */ |
248 | i40e_debug(hw, I40E_DEBUG_NVM, |
249 | "NVM read error: cannot spread over two sectors in a single read offset=%d words=%d\n" , |
250 | offset, words); |
251 | else |
252 | ret_code = i40e_aq_read_nvm(hw, module_pointer, |
253 | offset: 2 * offset, /*bytes*/ |
254 | length: 2 * words, /*bytes*/ |
255 | data, last_command, cmd_details: &cmd_details); |
256 | |
257 | return ret_code; |
258 | } |
259 | |
260 | /** |
261 | * i40e_read_nvm_word_aq - Reads Shadow RAM via AQ |
262 | * @hw: pointer to the HW structure |
263 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) |
264 | * @data: word read from the Shadow RAM |
265 | * |
266 | * Reads one 16 bit word from the Shadow RAM using the AdminQ |
267 | **/ |
268 | static int i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, |
269 | u16 *data) |
270 | { |
271 | int ret_code = -EIO; |
272 | |
273 | ret_code = i40e_read_nvm_aq(hw, module_pointer: 0x0, offset, words: 1, data, last_command: true); |
274 | *data = le16_to_cpu(*(__le16 *)data); |
275 | |
276 | return ret_code; |
277 | } |
278 | |
279 | /** |
280 | * __i40e_read_nvm_word - Reads nvm word, assumes caller does the locking |
281 | * @hw: pointer to the HW structure |
282 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) |
283 | * @data: word read from the Shadow RAM |
284 | * |
285 | * Reads one 16 bit word from the Shadow RAM. |
286 | * |
287 | * Do not use this function except in cases where the nvm lock is already |
288 | * taken via i40e_acquire_nvm(). |
289 | **/ |
290 | static int __i40e_read_nvm_word(struct i40e_hw *hw, |
291 | u16 offset, u16 *data) |
292 | { |
293 | if (test_bit(I40E_HW_CAP_AQ_SRCTL_ACCESS_ENABLE, hw->caps)) |
294 | return i40e_read_nvm_word_aq(hw, offset, data); |
295 | |
296 | return i40e_read_nvm_word_srctl(hw, offset, data); |
297 | } |
298 | |
299 | /** |
300 | * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary |
301 | * @hw: pointer to the HW structure |
302 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) |
303 | * @data: word read from the Shadow RAM |
304 | * |
305 | * Reads one 16 bit word from the Shadow RAM. |
306 | **/ |
307 | int i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, |
308 | u16 *data) |
309 | { |
310 | int ret_code = 0; |
311 | |
312 | if (test_bit(I40E_HW_CAP_NVM_READ_REQUIRES_LOCK, hw->caps)) |
313 | ret_code = i40e_acquire_nvm(hw, access: I40E_RESOURCE_READ); |
314 | if (ret_code) |
315 | return ret_code; |
316 | |
317 | ret_code = __i40e_read_nvm_word(hw, offset, data); |
318 | |
319 | if (test_bit(I40E_HW_CAP_NVM_READ_REQUIRES_LOCK, hw->caps)) |
320 | i40e_release_nvm(hw); |
321 | |
322 | return ret_code; |
323 | } |
324 | |
325 | /** |
326 | * i40e_read_nvm_module_data - Reads NVM Buffer to specified memory location |
327 | * @hw: Pointer to the HW structure |
328 | * @module_ptr: Pointer to module in words with respect to NVM beginning |
329 | * @module_offset: Offset in words from module start |
330 | * @data_offset: Offset in words from reading data area start |
331 | * @words_data_size: Words to read from NVM |
332 | * @data_ptr: Pointer to memory location where resulting buffer will be stored |
333 | **/ |
334 | int i40e_read_nvm_module_data(struct i40e_hw *hw, |
335 | u8 module_ptr, |
336 | u16 module_offset, |
337 | u16 data_offset, |
338 | u16 words_data_size, |
339 | u16 *data_ptr) |
340 | { |
341 | u16 specific_ptr = 0; |
342 | u16 ptr_value = 0; |
343 | u32 offset = 0; |
344 | int status; |
345 | |
346 | if (module_ptr != 0) { |
347 | status = i40e_read_nvm_word(hw, offset: module_ptr, data: &ptr_value); |
348 | if (status) { |
349 | i40e_debug(hw, I40E_DEBUG_ALL, |
350 | "Reading nvm word failed.Error code: %d.\n" , |
351 | status); |
352 | return -EIO; |
353 | } |
354 | } |
355 | #define I40E_NVM_INVALID_PTR_VAL 0x7FFF |
356 | #define I40E_NVM_INVALID_VAL 0xFFFF |
357 | |
358 | /* Pointer not initialized */ |
359 | if (ptr_value == I40E_NVM_INVALID_PTR_VAL || |
360 | ptr_value == I40E_NVM_INVALID_VAL) { |
361 | i40e_debug(hw, I40E_DEBUG_ALL, "Pointer not initialized.\n" ); |
362 | return -EINVAL; |
363 | } |
364 | |
365 | /* Check whether the module is in SR mapped area or outside */ |
366 | if (ptr_value & I40E_PTR_TYPE) { |
367 | /* Pointer points outside of the Shared RAM mapped area */ |
368 | i40e_debug(hw, I40E_DEBUG_ALL, |
369 | "Reading nvm data failed. Pointer points outside of the Shared RAM mapped area.\n" ); |
370 | |
371 | return -EINVAL; |
372 | } else { |
373 | /* Read from the Shadow RAM */ |
374 | |
375 | status = i40e_read_nvm_word(hw, offset: ptr_value + module_offset, |
376 | data: &specific_ptr); |
377 | if (status) { |
378 | i40e_debug(hw, I40E_DEBUG_ALL, |
379 | "Reading nvm word failed.Error code: %d.\n" , |
380 | status); |
381 | return -EIO; |
382 | } |
383 | |
384 | offset = ptr_value + module_offset + specific_ptr + |
385 | data_offset; |
386 | |
387 | status = i40e_read_nvm_buffer(hw, offset, words: &words_data_size, |
388 | data: data_ptr); |
389 | if (status) { |
390 | i40e_debug(hw, I40E_DEBUG_ALL, |
391 | "Reading nvm buffer failed.Error code: %d.\n" , |
392 | status); |
393 | } |
394 | } |
395 | |
396 | return status; |
397 | } |
398 | |
399 | /** |
400 | * i40e_read_nvm_buffer_srctl - Reads Shadow RAM buffer via SRCTL register |
401 | * @hw: pointer to the HW structure |
402 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). |
403 | * @words: (in) number of words to read; (out) number of words actually read |
404 | * @data: words read from the Shadow RAM |
405 | * |
406 | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() |
407 | * method. The buffer read is preceded by the NVM ownership take |
408 | * and followed by the release. |
409 | **/ |
410 | static int i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset, |
411 | u16 *words, u16 *data) |
412 | { |
413 | int ret_code = 0; |
414 | u16 index, word; |
415 | |
416 | /* Loop thru the selected region */ |
417 | for (word = 0; word < *words; word++) { |
418 | index = offset + word; |
419 | ret_code = i40e_read_nvm_word_srctl(hw, offset: index, data: &data[word]); |
420 | if (ret_code) |
421 | break; |
422 | } |
423 | |
424 | /* Update the number of words read from the Shadow RAM */ |
425 | *words = word; |
426 | |
427 | return ret_code; |
428 | } |
429 | |
430 | /** |
431 | * i40e_read_nvm_buffer_aq - Reads Shadow RAM buffer via AQ |
432 | * @hw: pointer to the HW structure |
433 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). |
434 | * @words: (in) number of words to read; (out) number of words actually read |
435 | * @data: words read from the Shadow RAM |
436 | * |
437 | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_aq() |
438 | * method. The buffer read is preceded by the NVM ownership take |
439 | * and followed by the release. |
440 | **/ |
441 | static int i40e_read_nvm_buffer_aq(struct i40e_hw *hw, u16 offset, |
442 | u16 *words, u16 *data) |
443 | { |
444 | bool last_cmd = false; |
445 | u16 words_read = 0; |
446 | u16 read_size; |
447 | int ret_code; |
448 | u16 i = 0; |
449 | |
450 | do { |
451 | /* Calculate number of bytes we should read in this step. |
452 | * FVL AQ do not allow to read more than one page at a time or |
453 | * to cross page boundaries. |
454 | */ |
455 | if (offset % I40E_SR_SECTOR_SIZE_IN_WORDS) |
456 | read_size = min(*words, |
457 | (u16)(I40E_SR_SECTOR_SIZE_IN_WORDS - |
458 | (offset % I40E_SR_SECTOR_SIZE_IN_WORDS))); |
459 | else |
460 | read_size = min((*words - words_read), |
461 | I40E_SR_SECTOR_SIZE_IN_WORDS); |
462 | |
463 | /* Check if this is last command, if so set proper flag */ |
464 | if ((words_read + read_size) >= *words) |
465 | last_cmd = true; |
466 | |
467 | ret_code = i40e_read_nvm_aq(hw, module_pointer: 0x0, offset, words: read_size, |
468 | data: data + words_read, last_command: last_cmd); |
469 | if (ret_code) |
470 | goto read_nvm_buffer_aq_exit; |
471 | |
472 | /* Increment counter for words already read and move offset to |
473 | * new read location |
474 | */ |
475 | words_read += read_size; |
476 | offset += read_size; |
477 | } while (words_read < *words); |
478 | |
479 | for (i = 0; i < *words; i++) |
480 | data[i] = le16_to_cpu(((__le16 *)data)[i]); |
481 | |
482 | read_nvm_buffer_aq_exit: |
483 | *words = words_read; |
484 | return ret_code; |
485 | } |
486 | |
487 | /** |
488 | * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock |
489 | * @hw: pointer to the HW structure |
490 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). |
491 | * @words: (in) number of words to read; (out) number of words actually read |
492 | * @data: words read from the Shadow RAM |
493 | * |
494 | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() |
495 | * method. |
496 | **/ |
497 | static int __i40e_read_nvm_buffer(struct i40e_hw *hw, |
498 | u16 offset, u16 *words, |
499 | u16 *data) |
500 | { |
501 | if (test_bit(I40E_HW_CAP_AQ_SRCTL_ACCESS_ENABLE, hw->caps)) |
502 | return i40e_read_nvm_buffer_aq(hw, offset, words, data); |
503 | |
504 | return i40e_read_nvm_buffer_srctl(hw, offset, words, data); |
505 | } |
506 | |
507 | /** |
508 | * i40e_read_nvm_buffer - Reads Shadow RAM buffer and acquire lock if necessary |
509 | * @hw: pointer to the HW structure |
510 | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). |
511 | * @words: (in) number of words to read; (out) number of words actually read |
512 | * @data: words read from the Shadow RAM |
513 | * |
514 | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() |
515 | * method. The buffer read is preceded by the NVM ownership take |
516 | * and followed by the release. |
517 | **/ |
518 | int i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, |
519 | u16 *words, u16 *data) |
520 | { |
521 | int ret_code = 0; |
522 | |
523 | if (test_bit(I40E_HW_CAP_AQ_SRCTL_ACCESS_ENABLE, hw->caps)) { |
524 | ret_code = i40e_acquire_nvm(hw, access: I40E_RESOURCE_READ); |
525 | if (!ret_code) { |
526 | ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, |
527 | data); |
528 | i40e_release_nvm(hw); |
529 | } |
530 | } else { |
531 | ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); |
532 | } |
533 | |
534 | return ret_code; |
535 | } |
536 | |
537 | /** |
538 | * i40e_write_nvm_aq - Writes Shadow RAM. |
539 | * @hw: pointer to the HW structure. |
540 | * @module_pointer: module pointer location in words from the NVM beginning |
541 | * @offset: offset in words from module start |
542 | * @words: number of words to write |
543 | * @data: buffer with words to write to the Shadow RAM |
544 | * @last_command: tells the AdminQ that this is the last command |
545 | * |
546 | * Writes a 16 bit words buffer to the Shadow RAM using the admin command. |
547 | **/ |
548 | static int i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, |
549 | u32 offset, u16 words, void *data, |
550 | bool last_command) |
551 | { |
552 | struct i40e_asq_cmd_details cmd_details; |
553 | int ret_code = -EIO; |
554 | |
555 | memset(&cmd_details, 0, sizeof(cmd_details)); |
556 | cmd_details.wb_desc = &hw->nvm_wb_desc; |
557 | |
558 | /* Here we are checking the SR limit only for the flat memory model. |
559 | * We cannot do it for the module-based model, as we did not acquire |
560 | * the NVM resource yet (we cannot get the module pointer value). |
561 | * Firmware will check the module-based model. |
562 | */ |
563 | if ((offset + words) > hw->nvm.sr_size) |
564 | i40e_debug(hw, I40E_DEBUG_NVM, |
565 | "NVM write error: offset %d beyond Shadow RAM limit %d\n" , |
566 | (offset + words), hw->nvm.sr_size); |
567 | else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS) |
568 | /* We can write only up to 4KB (one sector), in one AQ write */ |
569 | i40e_debug(hw, I40E_DEBUG_NVM, |
570 | "NVM write fail error: tried to write %d words, limit is %d.\n" , |
571 | words, I40E_SR_SECTOR_SIZE_IN_WORDS); |
572 | else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS) |
573 | != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS)) |
574 | /* A single write cannot spread over two sectors */ |
575 | i40e_debug(hw, I40E_DEBUG_NVM, |
576 | "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n" , |
577 | offset, words); |
578 | else |
579 | ret_code = i40e_aq_update_nvm(hw, module_pointer, |
580 | offset: 2 * offset, /*bytes*/ |
581 | length: 2 * words, /*bytes*/ |
582 | data, last_command, preservation_flags: 0, |
583 | cmd_details: &cmd_details); |
584 | |
585 | return ret_code; |
586 | } |
587 | |
588 | /** |
589 | * i40e_calc_nvm_checksum - Calculates and returns the checksum |
590 | * @hw: pointer to hardware structure |
591 | * @checksum: pointer to the checksum |
592 | * |
593 | * This function calculates SW Checksum that covers the whole 64kB shadow RAM |
594 | * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD |
595 | * is customer specific and unknown. Therefore, this function skips all maximum |
596 | * possible size of VPD (1kB). |
597 | **/ |
598 | static int i40e_calc_nvm_checksum(struct i40e_hw *hw, |
599 | u16 *checksum) |
600 | { |
601 | struct i40e_virt_mem vmem; |
602 | u16 pcie_alt_module = 0; |
603 | u16 checksum_local = 0; |
604 | u16 vpd_module = 0; |
605 | int ret_code; |
606 | u16 *data; |
607 | u16 i = 0; |
608 | |
609 | ret_code = i40e_allocate_virt_mem(hw, mem: &vmem, |
610 | I40E_SR_SECTOR_SIZE_IN_WORDS * sizeof(u16)); |
611 | if (ret_code) |
612 | goto i40e_calc_nvm_checksum_exit; |
613 | data = (u16 *)vmem.va; |
614 | |
615 | /* read pointer to VPD area */ |
616 | ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, data: &vpd_module); |
617 | if (ret_code) { |
618 | ret_code = -EIO; |
619 | goto i40e_calc_nvm_checksum_exit; |
620 | } |
621 | |
622 | /* read pointer to PCIe Alt Auto-load module */ |
623 | ret_code = __i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, |
624 | data: &pcie_alt_module); |
625 | if (ret_code) { |
626 | ret_code = -EIO; |
627 | goto i40e_calc_nvm_checksum_exit; |
628 | } |
629 | |
630 | /* Calculate SW checksum that covers the whole 64kB shadow RAM |
631 | * except the VPD and PCIe ALT Auto-load modules |
632 | */ |
633 | for (i = 0; i < hw->nvm.sr_size; i++) { |
634 | /* Read SR page */ |
635 | if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { |
636 | u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; |
637 | |
638 | ret_code = __i40e_read_nvm_buffer(hw, offset: i, words: &words, data); |
639 | if (ret_code) { |
640 | ret_code = -EIO; |
641 | goto i40e_calc_nvm_checksum_exit; |
642 | } |
643 | } |
644 | |
645 | /* Skip Checksum word */ |
646 | if (i == I40E_SR_SW_CHECKSUM_WORD) |
647 | continue; |
648 | /* Skip VPD module (convert byte size to word count) */ |
649 | if ((i >= (u32)vpd_module) && |
650 | (i < ((u32)vpd_module + |
651 | (I40E_SR_VPD_MODULE_MAX_SIZE / 2)))) { |
652 | continue; |
653 | } |
654 | /* Skip PCIe ALT module (convert byte size to word count) */ |
655 | if ((i >= (u32)pcie_alt_module) && |
656 | (i < ((u32)pcie_alt_module + |
657 | (I40E_SR_PCIE_ALT_MODULE_MAX_SIZE / 2)))) { |
658 | continue; |
659 | } |
660 | |
661 | checksum_local += data[i % I40E_SR_SECTOR_SIZE_IN_WORDS]; |
662 | } |
663 | |
664 | *checksum = (u16)I40E_SR_SW_CHECKSUM_BASE - checksum_local; |
665 | |
666 | i40e_calc_nvm_checksum_exit: |
667 | i40e_free_virt_mem(hw, mem: &vmem); |
668 | return ret_code; |
669 | } |
670 | |
671 | /** |
672 | * i40e_update_nvm_checksum - Updates the NVM checksum |
673 | * @hw: pointer to hardware structure |
674 | * |
675 | * NVM ownership must be acquired before calling this function and released |
676 | * on ARQ completion event reception by caller. |
677 | * This function will commit SR to NVM. |
678 | **/ |
679 | int i40e_update_nvm_checksum(struct i40e_hw *hw) |
680 | { |
681 | __le16 le_sum; |
682 | int ret_code; |
683 | u16 checksum; |
684 | |
685 | ret_code = i40e_calc_nvm_checksum(hw, checksum: &checksum); |
686 | if (!ret_code) { |
687 | le_sum = cpu_to_le16(checksum); |
688 | ret_code = i40e_write_nvm_aq(hw, module_pointer: 0x00, I40E_SR_SW_CHECKSUM_WORD, |
689 | words: 1, data: &le_sum, last_command: true); |
690 | } |
691 | |
692 | return ret_code; |
693 | } |
694 | |
695 | /** |
696 | * i40e_validate_nvm_checksum - Validate EEPROM checksum |
697 | * @hw: pointer to hardware structure |
698 | * @checksum: calculated checksum |
699 | * |
700 | * Performs checksum calculation and validates the NVM SW checksum. If the |
701 | * caller does not need checksum, the value can be NULL. |
702 | **/ |
703 | int i40e_validate_nvm_checksum(struct i40e_hw *hw, |
704 | u16 *checksum) |
705 | { |
706 | u16 checksum_local = 0; |
707 | u16 checksum_sr = 0; |
708 | int ret_code = 0; |
709 | |
710 | /* We must acquire the NVM lock in order to correctly synchronize the |
711 | * NVM accesses across multiple PFs. Without doing so it is possible |
712 | * for one of the PFs to read invalid data potentially indicating that |
713 | * the checksum is invalid. |
714 | */ |
715 | ret_code = i40e_acquire_nvm(hw, access: I40E_RESOURCE_READ); |
716 | if (ret_code) |
717 | return ret_code; |
718 | ret_code = i40e_calc_nvm_checksum(hw, checksum: &checksum_local); |
719 | __i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, data: &checksum_sr); |
720 | i40e_release_nvm(hw); |
721 | if (ret_code) |
722 | return ret_code; |
723 | |
724 | /* Verify read checksum from EEPROM is the same as |
725 | * calculated checksum |
726 | */ |
727 | if (checksum_local != checksum_sr) |
728 | ret_code = -EIO; |
729 | |
730 | /* If the user cares, return the calculated checksum */ |
731 | if (checksum) |
732 | *checksum = checksum_local; |
733 | |
734 | return ret_code; |
735 | } |
736 | |
737 | static int i40e_nvmupd_state_init(struct i40e_hw *hw, |
738 | struct i40e_nvm_access *cmd, |
739 | u8 *bytes, int *perrno); |
740 | static int i40e_nvmupd_state_reading(struct i40e_hw *hw, |
741 | struct i40e_nvm_access *cmd, |
742 | u8 *bytes, int *perrno); |
743 | static int i40e_nvmupd_state_writing(struct i40e_hw *hw, |
744 | struct i40e_nvm_access *cmd, |
745 | u8 *bytes, int *errno); |
746 | static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, |
747 | struct i40e_nvm_access *cmd, |
748 | int *perrno); |
749 | static int i40e_nvmupd_nvm_erase(struct i40e_hw *hw, |
750 | struct i40e_nvm_access *cmd, |
751 | int *perrno); |
752 | static int i40e_nvmupd_nvm_write(struct i40e_hw *hw, |
753 | struct i40e_nvm_access *cmd, |
754 | u8 *bytes, int *perrno); |
755 | static int i40e_nvmupd_nvm_read(struct i40e_hw *hw, |
756 | struct i40e_nvm_access *cmd, |
757 | u8 *bytes, int *perrno); |
758 | static int i40e_nvmupd_exec_aq(struct i40e_hw *hw, |
759 | struct i40e_nvm_access *cmd, |
760 | u8 *bytes, int *perrno); |
761 | static int i40e_nvmupd_get_aq_result(struct i40e_hw *hw, |
762 | struct i40e_nvm_access *cmd, |
763 | u8 *bytes, int *perrno); |
764 | static int i40e_nvmupd_get_aq_event(struct i40e_hw *hw, |
765 | struct i40e_nvm_access *cmd, |
766 | u8 *bytes, int *perrno); |
767 | static inline u8 i40e_nvmupd_get_module(u32 val) |
768 | { |
769 | return (u8)(val & I40E_NVM_MOD_PNT_MASK); |
770 | } |
771 | static inline u8 i40e_nvmupd_get_transaction(u32 val) |
772 | { |
773 | return FIELD_GET(I40E_NVM_TRANS_MASK, val); |
774 | } |
775 | |
776 | static inline u8 i40e_nvmupd_get_preservation_flags(u32 val) |
777 | { |
778 | return FIELD_GET(I40E_NVM_PRESERVATION_FLAGS_MASK, val); |
779 | } |
780 | |
781 | static const char * const i40e_nvm_update_state_str[] = { |
782 | "I40E_NVMUPD_INVALID" , |
783 | "I40E_NVMUPD_READ_CON" , |
784 | "I40E_NVMUPD_READ_SNT" , |
785 | "I40E_NVMUPD_READ_LCB" , |
786 | "I40E_NVMUPD_READ_SA" , |
787 | "I40E_NVMUPD_WRITE_ERA" , |
788 | "I40E_NVMUPD_WRITE_CON" , |
789 | "I40E_NVMUPD_WRITE_SNT" , |
790 | "I40E_NVMUPD_WRITE_LCB" , |
791 | "I40E_NVMUPD_WRITE_SA" , |
792 | "I40E_NVMUPD_CSUM_CON" , |
793 | "I40E_NVMUPD_CSUM_SA" , |
794 | "I40E_NVMUPD_CSUM_LCB" , |
795 | "I40E_NVMUPD_STATUS" , |
796 | "I40E_NVMUPD_EXEC_AQ" , |
797 | "I40E_NVMUPD_GET_AQ_RESULT" , |
798 | "I40E_NVMUPD_GET_AQ_EVENT" , |
799 | }; |
800 | |
801 | /** |
802 | * i40e_nvmupd_command - Process an NVM update command |
803 | * @hw: pointer to hardware structure |
804 | * @cmd: pointer to nvm update command |
805 | * @bytes: pointer to the data buffer |
806 | * @perrno: pointer to return error code |
807 | * |
808 | * Dispatches command depending on what update state is current |
809 | **/ |
810 | int i40e_nvmupd_command(struct i40e_hw *hw, |
811 | struct i40e_nvm_access *cmd, |
812 | u8 *bytes, int *perrno) |
813 | { |
814 | enum i40e_nvmupd_cmd upd_cmd; |
815 | int status; |
816 | |
817 | /* assume success */ |
818 | *perrno = 0; |
819 | |
820 | /* early check for status command and debug msgs */ |
821 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); |
822 | |
823 | i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d opc 0x%04x cmd 0x%08x config 0x%08x offset 0x%08x data_size 0x%08x\n" , |
824 | i40e_nvm_update_state_str[upd_cmd], |
825 | hw->nvmupd_state, |
826 | hw->nvm_release_on_done, hw->nvm_wait_opcode, |
827 | cmd->command, cmd->config, cmd->offset, cmd->data_size); |
828 | |
829 | if (upd_cmd == I40E_NVMUPD_INVALID) { |
830 | *perrno = -EFAULT; |
831 | i40e_debug(hw, I40E_DEBUG_NVM, |
832 | "i40e_nvmupd_validate_command returns %d errno %d\n" , |
833 | upd_cmd, *perrno); |
834 | } |
835 | |
836 | /* a status request returns immediately rather than |
837 | * going into the state machine |
838 | */ |
839 | if (upd_cmd == I40E_NVMUPD_STATUS) { |
840 | if (!cmd->data_size) { |
841 | *perrno = -EFAULT; |
842 | return -EINVAL; |
843 | } |
844 | |
845 | bytes[0] = hw->nvmupd_state; |
846 | |
847 | if (cmd->data_size >= 4) { |
848 | bytes[1] = 0; |
849 | *((u16 *)&bytes[2]) = hw->nvm_wait_opcode; |
850 | } |
851 | |
852 | /* Clear error status on read */ |
853 | if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) |
854 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
855 | |
856 | return 0; |
857 | } |
858 | |
859 | /* Clear status even it is not read and log */ |
860 | if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) { |
861 | i40e_debug(hw, I40E_DEBUG_NVM, |
862 | "Clearing I40E_NVMUPD_STATE_ERROR state without reading\n" ); |
863 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
864 | } |
865 | |
866 | /* Acquire lock to prevent race condition where adminq_task |
867 | * can execute after i40e_nvmupd_nvm_read/write but before state |
868 | * variables (nvm_wait_opcode, nvm_release_on_done) are updated. |
869 | * |
870 | * During NVMUpdate, it is observed that lock could be held for |
871 | * ~5ms for most commands. However lock is held for ~60ms for |
872 | * NVMUPD_CSUM_LCB command. |
873 | */ |
874 | mutex_lock(&hw->aq.arq_mutex); |
875 | switch (hw->nvmupd_state) { |
876 | case I40E_NVMUPD_STATE_INIT: |
877 | status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); |
878 | break; |
879 | |
880 | case I40E_NVMUPD_STATE_READING: |
881 | status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); |
882 | break; |
883 | |
884 | case I40E_NVMUPD_STATE_WRITING: |
885 | status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno: perrno); |
886 | break; |
887 | |
888 | case I40E_NVMUPD_STATE_INIT_WAIT: |
889 | case I40E_NVMUPD_STATE_WRITE_WAIT: |
890 | /* if we need to stop waiting for an event, clear |
891 | * the wait info and return before doing anything else |
892 | */ |
893 | if (cmd->offset == 0xffff) { |
894 | i40e_nvmupd_clear_wait_state(hw); |
895 | status = 0; |
896 | break; |
897 | } |
898 | |
899 | status = -EBUSY; |
900 | *perrno = -EBUSY; |
901 | break; |
902 | |
903 | default: |
904 | /* invalid state, should never happen */ |
905 | i40e_debug(hw, I40E_DEBUG_NVM, |
906 | "NVMUPD: no such state %d\n" , hw->nvmupd_state); |
907 | status = -EOPNOTSUPP; |
908 | *perrno = -ESRCH; |
909 | break; |
910 | } |
911 | |
912 | mutex_unlock(lock: &hw->aq.arq_mutex); |
913 | return status; |
914 | } |
915 | |
916 | /** |
917 | * i40e_nvmupd_state_init - Handle NVM update state Init |
918 | * @hw: pointer to hardware structure |
919 | * @cmd: pointer to nvm update command buffer |
920 | * @bytes: pointer to the data buffer |
921 | * @perrno: pointer to return error code |
922 | * |
923 | * Process legitimate commands of the Init state and conditionally set next |
924 | * state. Reject all other commands. |
925 | **/ |
926 | static int i40e_nvmupd_state_init(struct i40e_hw *hw, |
927 | struct i40e_nvm_access *cmd, |
928 | u8 *bytes, int *perrno) |
929 | { |
930 | enum i40e_nvmupd_cmd upd_cmd; |
931 | int status = 0; |
932 | |
933 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); |
934 | |
935 | switch (upd_cmd) { |
936 | case I40E_NVMUPD_READ_SA: |
937 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_READ); |
938 | if (status) { |
939 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, |
940 | aq_rc: hw->aq.asq_last_status); |
941 | } else { |
942 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); |
943 | i40e_release_nvm(hw); |
944 | } |
945 | break; |
946 | |
947 | case I40E_NVMUPD_READ_SNT: |
948 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_READ); |
949 | if (status) { |
950 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, |
951 | aq_rc: hw->aq.asq_last_status); |
952 | } else { |
953 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); |
954 | if (status) |
955 | i40e_release_nvm(hw); |
956 | else |
957 | hw->nvmupd_state = I40E_NVMUPD_STATE_READING; |
958 | } |
959 | break; |
960 | |
961 | case I40E_NVMUPD_WRITE_ERA: |
962 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_WRITE); |
963 | if (status) { |
964 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, |
965 | aq_rc: hw->aq.asq_last_status); |
966 | } else { |
967 | status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); |
968 | if (status) { |
969 | i40e_release_nvm(hw); |
970 | } else { |
971 | hw->nvm_release_on_done = true; |
972 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_erase; |
973 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; |
974 | } |
975 | } |
976 | break; |
977 | |
978 | case I40E_NVMUPD_WRITE_SA: |
979 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_WRITE); |
980 | if (status) { |
981 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, |
982 | aq_rc: hw->aq.asq_last_status); |
983 | } else { |
984 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); |
985 | if (status) { |
986 | i40e_release_nvm(hw); |
987 | } else { |
988 | hw->nvm_release_on_done = true; |
989 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
990 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; |
991 | } |
992 | } |
993 | break; |
994 | |
995 | case I40E_NVMUPD_WRITE_SNT: |
996 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_WRITE); |
997 | if (status) { |
998 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, |
999 | aq_rc: hw->aq.asq_last_status); |
1000 | } else { |
1001 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); |
1002 | if (status) { |
1003 | i40e_release_nvm(hw); |
1004 | } else { |
1005 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
1006 | hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; |
1007 | } |
1008 | } |
1009 | break; |
1010 | |
1011 | case I40E_NVMUPD_CSUM_SA: |
1012 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_WRITE); |
1013 | if (status) { |
1014 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, |
1015 | aq_rc: hw->aq.asq_last_status); |
1016 | } else { |
1017 | status = i40e_update_nvm_checksum(hw); |
1018 | if (status) { |
1019 | *perrno = hw->aq.asq_last_status ? |
1020 | i40e_aq_rc_to_posix(aq_ret: status, |
1021 | aq_rc: hw->aq.asq_last_status) : |
1022 | -EIO; |
1023 | i40e_release_nvm(hw); |
1024 | } else { |
1025 | hw->nvm_release_on_done = true; |
1026 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
1027 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; |
1028 | } |
1029 | } |
1030 | break; |
1031 | |
1032 | case I40E_NVMUPD_EXEC_AQ: |
1033 | status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); |
1034 | break; |
1035 | |
1036 | case I40E_NVMUPD_GET_AQ_RESULT: |
1037 | status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); |
1038 | break; |
1039 | |
1040 | case I40E_NVMUPD_GET_AQ_EVENT: |
1041 | status = i40e_nvmupd_get_aq_event(hw, cmd, bytes, perrno); |
1042 | break; |
1043 | |
1044 | default: |
1045 | i40e_debug(hw, I40E_DEBUG_NVM, |
1046 | "NVMUPD: bad cmd %s in init state\n" , |
1047 | i40e_nvm_update_state_str[upd_cmd]); |
1048 | status = -EIO; |
1049 | *perrno = -ESRCH; |
1050 | break; |
1051 | } |
1052 | return status; |
1053 | } |
1054 | |
1055 | /** |
1056 | * i40e_nvmupd_state_reading - Handle NVM update state Reading |
1057 | * @hw: pointer to hardware structure |
1058 | * @cmd: pointer to nvm update command buffer |
1059 | * @bytes: pointer to the data buffer |
1060 | * @perrno: pointer to return error code |
1061 | * |
1062 | * NVM ownership is already held. Process legitimate commands and set any |
1063 | * change in state; reject all other commands. |
1064 | **/ |
1065 | static int i40e_nvmupd_state_reading(struct i40e_hw *hw, |
1066 | struct i40e_nvm_access *cmd, |
1067 | u8 *bytes, int *perrno) |
1068 | { |
1069 | enum i40e_nvmupd_cmd upd_cmd; |
1070 | int status = 0; |
1071 | |
1072 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); |
1073 | |
1074 | switch (upd_cmd) { |
1075 | case I40E_NVMUPD_READ_SA: |
1076 | case I40E_NVMUPD_READ_CON: |
1077 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); |
1078 | break; |
1079 | |
1080 | case I40E_NVMUPD_READ_LCB: |
1081 | status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); |
1082 | i40e_release_nvm(hw); |
1083 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
1084 | break; |
1085 | |
1086 | default: |
1087 | i40e_debug(hw, I40E_DEBUG_NVM, |
1088 | "NVMUPD: bad cmd %s in reading state.\n" , |
1089 | i40e_nvm_update_state_str[upd_cmd]); |
1090 | status = -EOPNOTSUPP; |
1091 | *perrno = -ESRCH; |
1092 | break; |
1093 | } |
1094 | return status; |
1095 | } |
1096 | |
1097 | /** |
1098 | * i40e_nvmupd_state_writing - Handle NVM update state Writing |
1099 | * @hw: pointer to hardware structure |
1100 | * @cmd: pointer to nvm update command buffer |
1101 | * @bytes: pointer to the data buffer |
1102 | * @perrno: pointer to return error code |
1103 | * |
1104 | * NVM ownership is already held. Process legitimate commands and set any |
1105 | * change in state; reject all other commands |
1106 | **/ |
1107 | static int i40e_nvmupd_state_writing(struct i40e_hw *hw, |
1108 | struct i40e_nvm_access *cmd, |
1109 | u8 *bytes, int *perrno) |
1110 | { |
1111 | enum i40e_nvmupd_cmd upd_cmd; |
1112 | bool retry_attempt = false; |
1113 | int status = 0; |
1114 | |
1115 | upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); |
1116 | |
1117 | retry: |
1118 | switch (upd_cmd) { |
1119 | case I40E_NVMUPD_WRITE_CON: |
1120 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); |
1121 | if (!status) { |
1122 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
1123 | hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; |
1124 | } |
1125 | break; |
1126 | |
1127 | case I40E_NVMUPD_WRITE_LCB: |
1128 | status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); |
1129 | if (status) { |
1130 | *perrno = hw->aq.asq_last_status ? |
1131 | i40e_aq_rc_to_posix(aq_ret: status, |
1132 | aq_rc: hw->aq.asq_last_status) : |
1133 | -EIO; |
1134 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
1135 | } else { |
1136 | hw->nvm_release_on_done = true; |
1137 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
1138 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; |
1139 | } |
1140 | break; |
1141 | |
1142 | case I40E_NVMUPD_CSUM_CON: |
1143 | /* Assumes the caller has acquired the nvm */ |
1144 | status = i40e_update_nvm_checksum(hw); |
1145 | if (status) { |
1146 | *perrno = hw->aq.asq_last_status ? |
1147 | i40e_aq_rc_to_posix(aq_ret: status, |
1148 | aq_rc: hw->aq.asq_last_status) : |
1149 | -EIO; |
1150 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
1151 | } else { |
1152 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
1153 | hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; |
1154 | } |
1155 | break; |
1156 | |
1157 | case I40E_NVMUPD_CSUM_LCB: |
1158 | /* Assumes the caller has acquired the nvm */ |
1159 | status = i40e_update_nvm_checksum(hw); |
1160 | if (status) { |
1161 | *perrno = hw->aq.asq_last_status ? |
1162 | i40e_aq_rc_to_posix(aq_ret: status, |
1163 | aq_rc: hw->aq.asq_last_status) : |
1164 | -EIO; |
1165 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
1166 | } else { |
1167 | hw->nvm_release_on_done = true; |
1168 | hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; |
1169 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; |
1170 | } |
1171 | break; |
1172 | |
1173 | default: |
1174 | i40e_debug(hw, I40E_DEBUG_NVM, |
1175 | "NVMUPD: bad cmd %s in writing state.\n" , |
1176 | i40e_nvm_update_state_str[upd_cmd]); |
1177 | status = -EOPNOTSUPP; |
1178 | *perrno = -ESRCH; |
1179 | break; |
1180 | } |
1181 | |
1182 | /* In some circumstances, a multi-write transaction takes longer |
1183 | * than the default 3 minute timeout on the write semaphore. If |
1184 | * the write failed with an EBUSY status, this is likely the problem, |
1185 | * so here we try to reacquire the semaphore then retry the write. |
1186 | * We only do one retry, then give up. |
1187 | */ |
1188 | if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) && |
1189 | !retry_attempt) { |
1190 | u32 old_asq_status = hw->aq.asq_last_status; |
1191 | int old_status = status; |
1192 | u32 gtime; |
1193 | |
1194 | gtime = rd32(hw, I40E_GLVFGEN_TIMER); |
1195 | if (gtime >= hw->nvm.hw_semaphore_timeout) { |
1196 | i40e_debug(hw, I40E_DEBUG_ALL, |
1197 | "NVMUPD: write semaphore expired (%d >= %lld), retrying\n" , |
1198 | gtime, hw->nvm.hw_semaphore_timeout); |
1199 | i40e_release_nvm(hw); |
1200 | status = i40e_acquire_nvm(hw, access: I40E_RESOURCE_WRITE); |
1201 | if (status) { |
1202 | i40e_debug(hw, I40E_DEBUG_ALL, |
1203 | "NVMUPD: write semaphore reacquire failed aq_err = %d\n" , |
1204 | hw->aq.asq_last_status); |
1205 | status = old_status; |
1206 | hw->aq.asq_last_status = old_asq_status; |
1207 | } else { |
1208 | retry_attempt = true; |
1209 | goto retry; |
1210 | } |
1211 | } |
1212 | } |
1213 | |
1214 | return status; |
1215 | } |
1216 | |
1217 | /** |
1218 | * i40e_nvmupd_clear_wait_state - clear wait state on hw |
1219 | * @hw: pointer to the hardware structure |
1220 | **/ |
1221 | void i40e_nvmupd_clear_wait_state(struct i40e_hw *hw) |
1222 | { |
1223 | i40e_debug(hw, I40E_DEBUG_NVM, |
1224 | "NVMUPD: clearing wait on opcode 0x%04x\n" , |
1225 | hw->nvm_wait_opcode); |
1226 | |
1227 | if (hw->nvm_release_on_done) { |
1228 | i40e_release_nvm(hw); |
1229 | hw->nvm_release_on_done = false; |
1230 | } |
1231 | hw->nvm_wait_opcode = 0; |
1232 | |
1233 | if (hw->aq.arq_last_status) { |
1234 | hw->nvmupd_state = I40E_NVMUPD_STATE_ERROR; |
1235 | return; |
1236 | } |
1237 | |
1238 | switch (hw->nvmupd_state) { |
1239 | case I40E_NVMUPD_STATE_INIT_WAIT: |
1240 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; |
1241 | break; |
1242 | |
1243 | case I40E_NVMUPD_STATE_WRITE_WAIT: |
1244 | hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; |
1245 | break; |
1246 | |
1247 | default: |
1248 | break; |
1249 | } |
1250 | } |
1251 | |
1252 | /** |
1253 | * i40e_nvmupd_check_wait_event - handle NVM update operation events |
1254 | * @hw: pointer to the hardware structure |
1255 | * @opcode: the event that just happened |
1256 | * @desc: AdminQ descriptor |
1257 | **/ |
1258 | void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode, |
1259 | struct i40e_aq_desc *desc) |
1260 | { |
1261 | u32 aq_desc_len = sizeof(struct i40e_aq_desc); |
1262 | |
1263 | if (opcode == hw->nvm_wait_opcode) { |
1264 | memcpy(&hw->nvm_aq_event_desc, desc, aq_desc_len); |
1265 | i40e_nvmupd_clear_wait_state(hw); |
1266 | } |
1267 | } |
1268 | |
1269 | /** |
1270 | * i40e_nvmupd_validate_command - Validate given command |
1271 | * @hw: pointer to hardware structure |
1272 | * @cmd: pointer to nvm update command buffer |
1273 | * @perrno: pointer to return error code |
1274 | * |
1275 | * Return one of the valid command types or I40E_NVMUPD_INVALID |
1276 | **/ |
1277 | static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, |
1278 | struct i40e_nvm_access *cmd, |
1279 | int *perrno) |
1280 | { |
1281 | enum i40e_nvmupd_cmd upd_cmd; |
1282 | u8 module, transaction; |
1283 | |
1284 | /* anything that doesn't match a recognized case is an error */ |
1285 | upd_cmd = I40E_NVMUPD_INVALID; |
1286 | |
1287 | transaction = i40e_nvmupd_get_transaction(val: cmd->config); |
1288 | module = i40e_nvmupd_get_module(val: cmd->config); |
1289 | |
1290 | /* limits on data size */ |
1291 | if ((cmd->data_size < 1) || |
1292 | (cmd->data_size > I40E_NVMUPD_MAX_DATA)) { |
1293 | i40e_debug(hw, I40E_DEBUG_NVM, |
1294 | "i40e_nvmupd_validate_command data_size %d\n" , |
1295 | cmd->data_size); |
1296 | *perrno = -EFAULT; |
1297 | return I40E_NVMUPD_INVALID; |
1298 | } |
1299 | |
1300 | switch (cmd->command) { |
1301 | case I40E_NVM_READ: |
1302 | switch (transaction) { |
1303 | case I40E_NVM_CON: |
1304 | upd_cmd = I40E_NVMUPD_READ_CON; |
1305 | break; |
1306 | case I40E_NVM_SNT: |
1307 | upd_cmd = I40E_NVMUPD_READ_SNT; |
1308 | break; |
1309 | case I40E_NVM_LCB: |
1310 | upd_cmd = I40E_NVMUPD_READ_LCB; |
1311 | break; |
1312 | case I40E_NVM_SA: |
1313 | upd_cmd = I40E_NVMUPD_READ_SA; |
1314 | break; |
1315 | case I40E_NVM_EXEC: |
1316 | if (module == 0xf) |
1317 | upd_cmd = I40E_NVMUPD_STATUS; |
1318 | else if (module == 0) |
1319 | upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; |
1320 | break; |
1321 | case I40E_NVM_AQE: |
1322 | upd_cmd = I40E_NVMUPD_GET_AQ_EVENT; |
1323 | break; |
1324 | } |
1325 | break; |
1326 | |
1327 | case I40E_NVM_WRITE: |
1328 | switch (transaction) { |
1329 | case I40E_NVM_CON: |
1330 | upd_cmd = I40E_NVMUPD_WRITE_CON; |
1331 | break; |
1332 | case I40E_NVM_SNT: |
1333 | upd_cmd = I40E_NVMUPD_WRITE_SNT; |
1334 | break; |
1335 | case I40E_NVM_LCB: |
1336 | upd_cmd = I40E_NVMUPD_WRITE_LCB; |
1337 | break; |
1338 | case I40E_NVM_SA: |
1339 | upd_cmd = I40E_NVMUPD_WRITE_SA; |
1340 | break; |
1341 | case I40E_NVM_ERA: |
1342 | upd_cmd = I40E_NVMUPD_WRITE_ERA; |
1343 | break; |
1344 | case I40E_NVM_CSUM: |
1345 | upd_cmd = I40E_NVMUPD_CSUM_CON; |
1346 | break; |
1347 | case (I40E_NVM_CSUM|I40E_NVM_SA): |
1348 | upd_cmd = I40E_NVMUPD_CSUM_SA; |
1349 | break; |
1350 | case (I40E_NVM_CSUM|I40E_NVM_LCB): |
1351 | upd_cmd = I40E_NVMUPD_CSUM_LCB; |
1352 | break; |
1353 | case I40E_NVM_EXEC: |
1354 | if (module == 0) |
1355 | upd_cmd = I40E_NVMUPD_EXEC_AQ; |
1356 | break; |
1357 | } |
1358 | break; |
1359 | } |
1360 | |
1361 | return upd_cmd; |
1362 | } |
1363 | |
1364 | /** |
1365 | * i40e_nvmupd_exec_aq - Run an AQ command |
1366 | * @hw: pointer to hardware structure |
1367 | * @cmd: pointer to nvm update command buffer |
1368 | * @bytes: pointer to the data buffer |
1369 | * @perrno: pointer to return error code |
1370 | * |
1371 | * cmd structure contains identifiers and data buffer |
1372 | **/ |
1373 | static int i40e_nvmupd_exec_aq(struct i40e_hw *hw, |
1374 | struct i40e_nvm_access *cmd, |
1375 | u8 *bytes, int *perrno) |
1376 | { |
1377 | struct i40e_asq_cmd_details cmd_details; |
1378 | struct i40e_aq_desc *aq_desc; |
1379 | u32 buff_size = 0; |
1380 | u8 *buff = NULL; |
1381 | u32 aq_desc_len; |
1382 | u32 aq_data_len; |
1383 | int status; |
1384 | |
1385 | i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n" , __func__); |
1386 | if (cmd->offset == 0xffff) |
1387 | return 0; |
1388 | |
1389 | memset(&cmd_details, 0, sizeof(cmd_details)); |
1390 | cmd_details.wb_desc = &hw->nvm_wb_desc; |
1391 | |
1392 | aq_desc_len = sizeof(struct i40e_aq_desc); |
1393 | memset(&hw->nvm_wb_desc, 0, aq_desc_len); |
1394 | |
1395 | /* get the aq descriptor */ |
1396 | if (cmd->data_size < aq_desc_len) { |
1397 | i40e_debug(hw, I40E_DEBUG_NVM, |
1398 | "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n" , |
1399 | cmd->data_size, aq_desc_len); |
1400 | *perrno = -EINVAL; |
1401 | return -EINVAL; |
1402 | } |
1403 | aq_desc = (struct i40e_aq_desc *)bytes; |
1404 | |
1405 | /* if data buffer needed, make sure it's ready */ |
1406 | aq_data_len = cmd->data_size - aq_desc_len; |
1407 | buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen)); |
1408 | if (buff_size) { |
1409 | if (!hw->nvm_buff.va) { |
1410 | status = i40e_allocate_virt_mem(hw, mem: &hw->nvm_buff, |
1411 | size: hw->aq.asq_buf_size); |
1412 | if (status) |
1413 | i40e_debug(hw, I40E_DEBUG_NVM, |
1414 | "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n" , |
1415 | status); |
1416 | } |
1417 | |
1418 | if (hw->nvm_buff.va) { |
1419 | buff = hw->nvm_buff.va; |
1420 | memcpy(buff, &bytes[aq_desc_len], aq_data_len); |
1421 | } |
1422 | } |
1423 | |
1424 | if (cmd->offset) |
1425 | memset(&hw->nvm_aq_event_desc, 0, aq_desc_len); |
1426 | |
1427 | /* and away we go! */ |
1428 | status = i40e_asq_send_command(hw, desc: aq_desc, buff, |
1429 | buff_size, cmd_details: &cmd_details); |
1430 | if (status) { |
1431 | i40e_debug(hw, I40E_DEBUG_NVM, |
1432 | "%s err %pe aq_err %s\n" , |
1433 | __func__, ERR_PTR(status), |
1434 | i40e_aq_str(hw, hw->aq.asq_last_status)); |
1435 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, aq_rc: hw->aq.asq_last_status); |
1436 | return status; |
1437 | } |
1438 | |
1439 | /* should we wait for a followup event? */ |
1440 | if (cmd->offset) { |
1441 | hw->nvm_wait_opcode = cmd->offset; |
1442 | hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; |
1443 | } |
1444 | |
1445 | return status; |
1446 | } |
1447 | |
1448 | /** |
1449 | * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq |
1450 | * @hw: pointer to hardware structure |
1451 | * @cmd: pointer to nvm update command buffer |
1452 | * @bytes: pointer to the data buffer |
1453 | * @perrno: pointer to return error code |
1454 | * |
1455 | * cmd structure contains identifiers and data buffer |
1456 | **/ |
1457 | static int i40e_nvmupd_get_aq_result(struct i40e_hw *hw, |
1458 | struct i40e_nvm_access *cmd, |
1459 | u8 *bytes, int *perrno) |
1460 | { |
1461 | u32 aq_total_len; |
1462 | u32 aq_desc_len; |
1463 | int remainder; |
1464 | u8 *buff; |
1465 | |
1466 | i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n" , __func__); |
1467 | |
1468 | aq_desc_len = sizeof(struct i40e_aq_desc); |
1469 | aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen); |
1470 | |
1471 | /* check offset range */ |
1472 | if (cmd->offset > aq_total_len) { |
1473 | i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n" , |
1474 | __func__, cmd->offset, aq_total_len); |
1475 | *perrno = -EINVAL; |
1476 | return -EINVAL; |
1477 | } |
1478 | |
1479 | /* check copylength range */ |
1480 | if (cmd->data_size > (aq_total_len - cmd->offset)) { |
1481 | int new_len = aq_total_len - cmd->offset; |
1482 | |
1483 | i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n" , |
1484 | __func__, cmd->data_size, new_len); |
1485 | cmd->data_size = new_len; |
1486 | } |
1487 | |
1488 | remainder = cmd->data_size; |
1489 | if (cmd->offset < aq_desc_len) { |
1490 | u32 len = aq_desc_len - cmd->offset; |
1491 | |
1492 | len = min(len, cmd->data_size); |
1493 | i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n" , |
1494 | __func__, cmd->offset, cmd->offset + len); |
1495 | |
1496 | buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; |
1497 | memcpy(bytes, buff, len); |
1498 | |
1499 | bytes += len; |
1500 | remainder -= len; |
1501 | buff = hw->nvm_buff.va; |
1502 | } else { |
1503 | buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len); |
1504 | } |
1505 | |
1506 | if (remainder > 0) { |
1507 | int start_byte = buff - (u8 *)hw->nvm_buff.va; |
1508 | |
1509 | i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n" , |
1510 | __func__, start_byte, start_byte + remainder); |
1511 | memcpy(bytes, buff, remainder); |
1512 | } |
1513 | |
1514 | return 0; |
1515 | } |
1516 | |
1517 | /** |
1518 | * i40e_nvmupd_get_aq_event - Get the Admin Queue event from previous exec_aq |
1519 | * @hw: pointer to hardware structure |
1520 | * @cmd: pointer to nvm update command buffer |
1521 | * @bytes: pointer to the data buffer |
1522 | * @perrno: pointer to return error code |
1523 | * |
1524 | * cmd structure contains identifiers and data buffer |
1525 | **/ |
1526 | static int i40e_nvmupd_get_aq_event(struct i40e_hw *hw, |
1527 | struct i40e_nvm_access *cmd, |
1528 | u8 *bytes, int *perrno) |
1529 | { |
1530 | u32 aq_total_len; |
1531 | u32 aq_desc_len; |
1532 | |
1533 | i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n" , __func__); |
1534 | |
1535 | aq_desc_len = sizeof(struct i40e_aq_desc); |
1536 | aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_aq_event_desc.datalen); |
1537 | |
1538 | /* check copylength range */ |
1539 | if (cmd->data_size > aq_total_len) { |
1540 | i40e_debug(hw, I40E_DEBUG_NVM, |
1541 | "%s: copy length %d too big, trimming to %d\n" , |
1542 | __func__, cmd->data_size, aq_total_len); |
1543 | cmd->data_size = aq_total_len; |
1544 | } |
1545 | |
1546 | memcpy(bytes, &hw->nvm_aq_event_desc, cmd->data_size); |
1547 | |
1548 | return 0; |
1549 | } |
1550 | |
1551 | /** |
1552 | * i40e_nvmupd_nvm_read - Read NVM |
1553 | * @hw: pointer to hardware structure |
1554 | * @cmd: pointer to nvm update command buffer |
1555 | * @bytes: pointer to the data buffer |
1556 | * @perrno: pointer to return error code |
1557 | * |
1558 | * cmd structure contains identifiers and data buffer |
1559 | **/ |
1560 | static int i40e_nvmupd_nvm_read(struct i40e_hw *hw, |
1561 | struct i40e_nvm_access *cmd, |
1562 | u8 *bytes, int *perrno) |
1563 | { |
1564 | struct i40e_asq_cmd_details cmd_details; |
1565 | u8 module, transaction; |
1566 | int status; |
1567 | bool last; |
1568 | |
1569 | transaction = i40e_nvmupd_get_transaction(val: cmd->config); |
1570 | module = i40e_nvmupd_get_module(val: cmd->config); |
1571 | last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); |
1572 | |
1573 | memset(&cmd_details, 0, sizeof(cmd_details)); |
1574 | cmd_details.wb_desc = &hw->nvm_wb_desc; |
1575 | |
1576 | status = i40e_aq_read_nvm(hw, module_pointer: module, offset: cmd->offset, length: (u16)cmd->data_size, |
1577 | data: bytes, last_command: last, cmd_details: &cmd_details); |
1578 | if (status) { |
1579 | i40e_debug(hw, I40E_DEBUG_NVM, |
1580 | "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n" , |
1581 | module, cmd->offset, cmd->data_size); |
1582 | i40e_debug(hw, I40E_DEBUG_NVM, |
1583 | "i40e_nvmupd_nvm_read status %d aq %d\n" , |
1584 | status, hw->aq.asq_last_status); |
1585 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, aq_rc: hw->aq.asq_last_status); |
1586 | } |
1587 | |
1588 | return status; |
1589 | } |
1590 | |
1591 | /** |
1592 | * i40e_nvmupd_nvm_erase - Erase an NVM module |
1593 | * @hw: pointer to hardware structure |
1594 | * @cmd: pointer to nvm update command buffer |
1595 | * @perrno: pointer to return error code |
1596 | * |
1597 | * module, offset, data_size and data are in cmd structure |
1598 | **/ |
1599 | static int i40e_nvmupd_nvm_erase(struct i40e_hw *hw, |
1600 | struct i40e_nvm_access *cmd, |
1601 | int *perrno) |
1602 | { |
1603 | struct i40e_asq_cmd_details cmd_details; |
1604 | u8 module, transaction; |
1605 | int status = 0; |
1606 | bool last; |
1607 | |
1608 | transaction = i40e_nvmupd_get_transaction(val: cmd->config); |
1609 | module = i40e_nvmupd_get_module(val: cmd->config); |
1610 | last = (transaction & I40E_NVM_LCB); |
1611 | |
1612 | memset(&cmd_details, 0, sizeof(cmd_details)); |
1613 | cmd_details.wb_desc = &hw->nvm_wb_desc; |
1614 | |
1615 | status = i40e_aq_erase_nvm(hw, module_pointer: module, offset: cmd->offset, length: (u16)cmd->data_size, |
1616 | last_command: last, cmd_details: &cmd_details); |
1617 | if (status) { |
1618 | i40e_debug(hw, I40E_DEBUG_NVM, |
1619 | "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n" , |
1620 | module, cmd->offset, cmd->data_size); |
1621 | i40e_debug(hw, I40E_DEBUG_NVM, |
1622 | "i40e_nvmupd_nvm_erase status %d aq %d\n" , |
1623 | status, hw->aq.asq_last_status); |
1624 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, aq_rc: hw->aq.asq_last_status); |
1625 | } |
1626 | |
1627 | return status; |
1628 | } |
1629 | |
1630 | /** |
1631 | * i40e_nvmupd_nvm_write - Write NVM |
1632 | * @hw: pointer to hardware structure |
1633 | * @cmd: pointer to nvm update command buffer |
1634 | * @bytes: pointer to the data buffer |
1635 | * @perrno: pointer to return error code |
1636 | * |
1637 | * module, offset, data_size and data are in cmd structure |
1638 | **/ |
1639 | static int i40e_nvmupd_nvm_write(struct i40e_hw *hw, |
1640 | struct i40e_nvm_access *cmd, |
1641 | u8 *bytes, int *perrno) |
1642 | { |
1643 | struct i40e_asq_cmd_details cmd_details; |
1644 | u8 module, transaction; |
1645 | u8 preservation_flags; |
1646 | int status = 0; |
1647 | bool last; |
1648 | |
1649 | transaction = i40e_nvmupd_get_transaction(val: cmd->config); |
1650 | module = i40e_nvmupd_get_module(val: cmd->config); |
1651 | last = (transaction & I40E_NVM_LCB); |
1652 | preservation_flags = i40e_nvmupd_get_preservation_flags(val: cmd->config); |
1653 | |
1654 | memset(&cmd_details, 0, sizeof(cmd_details)); |
1655 | cmd_details.wb_desc = &hw->nvm_wb_desc; |
1656 | |
1657 | status = i40e_aq_update_nvm(hw, module_pointer: module, offset: cmd->offset, |
1658 | length: (u16)cmd->data_size, data: bytes, last_command: last, |
1659 | preservation_flags, cmd_details: &cmd_details); |
1660 | if (status) { |
1661 | i40e_debug(hw, I40E_DEBUG_NVM, |
1662 | "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n" , |
1663 | module, cmd->offset, cmd->data_size); |
1664 | i40e_debug(hw, I40E_DEBUG_NVM, |
1665 | "i40e_nvmupd_nvm_write status %d aq %d\n" , |
1666 | status, hw->aq.asq_last_status); |
1667 | *perrno = i40e_aq_rc_to_posix(aq_ret: status, aq_rc: hw->aq.asq_last_status); |
1668 | } |
1669 | |
1670 | return status; |
1671 | } |
1672 | |