1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 1999 - 2018 Intel Corporation. */ |
3 | |
4 | #include "e1000.h" |
5 | |
6 | /** |
7 | * e1000_raise_eec_clk - Raise EEPROM clock |
8 | * @hw: pointer to the HW structure |
9 | * @eecd: pointer to the EEPROM |
10 | * |
11 | * Enable/Raise the EEPROM clock bit. |
12 | **/ |
13 | static void e1000_raise_eec_clk(struct e1000_hw *hw, u32 *eecd) |
14 | { |
15 | *eecd = *eecd | E1000_EECD_SK; |
16 | ew32(EECD, *eecd); |
17 | e1e_flush(); |
18 | udelay(hw->nvm.delay_usec); |
19 | } |
20 | |
21 | /** |
22 | * e1000_lower_eec_clk - Lower EEPROM clock |
23 | * @hw: pointer to the HW structure |
24 | * @eecd: pointer to the EEPROM |
25 | * |
26 | * Clear/Lower the EEPROM clock bit. |
27 | **/ |
28 | static void e1000_lower_eec_clk(struct e1000_hw *hw, u32 *eecd) |
29 | { |
30 | *eecd = *eecd & ~E1000_EECD_SK; |
31 | ew32(EECD, *eecd); |
32 | e1e_flush(); |
33 | udelay(hw->nvm.delay_usec); |
34 | } |
35 | |
36 | /** |
37 | * e1000_shift_out_eec_bits - Shift data bits our to the EEPROM |
38 | * @hw: pointer to the HW structure |
39 | * @data: data to send to the EEPROM |
40 | * @count: number of bits to shift out |
41 | * |
42 | * We need to shift 'count' bits out to the EEPROM. So, the value in the |
43 | * "data" parameter will be shifted out to the EEPROM one bit at a time. |
44 | * In order to do this, "data" must be broken down into bits. |
45 | **/ |
46 | static void e1000_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) |
47 | { |
48 | struct e1000_nvm_info *nvm = &hw->nvm; |
49 | u32 eecd = er32(EECD); |
50 | u32 mask; |
51 | |
52 | mask = BIT(count - 1); |
53 | if (nvm->type == e1000_nvm_eeprom_spi) |
54 | eecd |= E1000_EECD_DO; |
55 | |
56 | do { |
57 | eecd &= ~E1000_EECD_DI; |
58 | |
59 | if (data & mask) |
60 | eecd |= E1000_EECD_DI; |
61 | |
62 | ew32(EECD, eecd); |
63 | e1e_flush(); |
64 | |
65 | udelay(nvm->delay_usec); |
66 | |
67 | e1000_raise_eec_clk(hw, eecd: &eecd); |
68 | e1000_lower_eec_clk(hw, eecd: &eecd); |
69 | |
70 | mask >>= 1; |
71 | } while (mask); |
72 | |
73 | eecd &= ~E1000_EECD_DI; |
74 | ew32(EECD, eecd); |
75 | } |
76 | |
77 | /** |
78 | * e1000_shift_in_eec_bits - Shift data bits in from the EEPROM |
79 | * @hw: pointer to the HW structure |
80 | * @count: number of bits to shift in |
81 | * |
82 | * In order to read a register from the EEPROM, we need to shift 'count' bits |
83 | * in from the EEPROM. Bits are "shifted in" by raising the clock input to |
84 | * the EEPROM (setting the SK bit), and then reading the value of the data out |
85 | * "DO" bit. During this "shifting in" process the data in "DI" bit should |
86 | * always be clear. |
87 | **/ |
88 | static u16 e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count) |
89 | { |
90 | u32 eecd; |
91 | u32 i; |
92 | u16 data; |
93 | |
94 | eecd = er32(EECD); |
95 | eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); |
96 | data = 0; |
97 | |
98 | for (i = 0; i < count; i++) { |
99 | data <<= 1; |
100 | e1000_raise_eec_clk(hw, eecd: &eecd); |
101 | |
102 | eecd = er32(EECD); |
103 | |
104 | eecd &= ~E1000_EECD_DI; |
105 | if (eecd & E1000_EECD_DO) |
106 | data |= 1; |
107 | |
108 | e1000_lower_eec_clk(hw, eecd: &eecd); |
109 | } |
110 | |
111 | return data; |
112 | } |
113 | |
114 | /** |
115 | * e1000e_poll_eerd_eewr_done - Poll for EEPROM read/write completion |
116 | * @hw: pointer to the HW structure |
117 | * @ee_reg: EEPROM flag for polling |
118 | * |
119 | * Polls the EEPROM status bit for either read or write completion based |
120 | * upon the value of 'ee_reg'. |
121 | **/ |
122 | s32 e1000e_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg) |
123 | { |
124 | u32 attempts = 100000; |
125 | u32 i, reg = 0; |
126 | |
127 | for (i = 0; i < attempts; i++) { |
128 | if (ee_reg == E1000_NVM_POLL_READ) |
129 | reg = er32(EERD); |
130 | else |
131 | reg = er32(EEWR); |
132 | |
133 | if (reg & E1000_NVM_RW_REG_DONE) |
134 | return 0; |
135 | |
136 | udelay(5); |
137 | } |
138 | |
139 | return -E1000_ERR_NVM; |
140 | } |
141 | |
142 | /** |
143 | * e1000e_acquire_nvm - Generic request for access to EEPROM |
144 | * @hw: pointer to the HW structure |
145 | * |
146 | * Set the EEPROM access request bit and wait for EEPROM access grant bit. |
147 | * Return successful if access grant bit set, else clear the request for |
148 | * EEPROM access and return -E1000_ERR_NVM (-1). |
149 | **/ |
150 | s32 e1000e_acquire_nvm(struct e1000_hw *hw) |
151 | { |
152 | u32 eecd = er32(EECD); |
153 | s32 timeout = E1000_NVM_GRANT_ATTEMPTS; |
154 | |
155 | ew32(EECD, eecd | E1000_EECD_REQ); |
156 | eecd = er32(EECD); |
157 | |
158 | while (timeout) { |
159 | if (eecd & E1000_EECD_GNT) |
160 | break; |
161 | udelay(5); |
162 | eecd = er32(EECD); |
163 | timeout--; |
164 | } |
165 | |
166 | if (!timeout) { |
167 | eecd &= ~E1000_EECD_REQ; |
168 | ew32(EECD, eecd); |
169 | e_dbg("Could not acquire NVM grant\n" ); |
170 | return -E1000_ERR_NVM; |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | /** |
177 | * e1000_standby_nvm - Return EEPROM to standby state |
178 | * @hw: pointer to the HW structure |
179 | * |
180 | * Return the EEPROM to a standby state. |
181 | **/ |
182 | static void e1000_standby_nvm(struct e1000_hw *hw) |
183 | { |
184 | struct e1000_nvm_info *nvm = &hw->nvm; |
185 | u32 eecd = er32(EECD); |
186 | |
187 | if (nvm->type == e1000_nvm_eeprom_spi) { |
188 | /* Toggle CS to flush commands */ |
189 | eecd |= E1000_EECD_CS; |
190 | ew32(EECD, eecd); |
191 | e1e_flush(); |
192 | udelay(nvm->delay_usec); |
193 | eecd &= ~E1000_EECD_CS; |
194 | ew32(EECD, eecd); |
195 | e1e_flush(); |
196 | udelay(nvm->delay_usec); |
197 | } |
198 | } |
199 | |
200 | /** |
201 | * e1000_stop_nvm - Terminate EEPROM command |
202 | * @hw: pointer to the HW structure |
203 | * |
204 | * Terminates the current command by inverting the EEPROM's chip select pin. |
205 | **/ |
206 | static void e1000_stop_nvm(struct e1000_hw *hw) |
207 | { |
208 | u32 eecd; |
209 | |
210 | eecd = er32(EECD); |
211 | if (hw->nvm.type == e1000_nvm_eeprom_spi) { |
212 | /* Pull CS high */ |
213 | eecd |= E1000_EECD_CS; |
214 | e1000_lower_eec_clk(hw, eecd: &eecd); |
215 | } |
216 | } |
217 | |
218 | /** |
219 | * e1000e_release_nvm - Release exclusive access to EEPROM |
220 | * @hw: pointer to the HW structure |
221 | * |
222 | * Stop any current commands to the EEPROM and clear the EEPROM request bit. |
223 | **/ |
224 | void e1000e_release_nvm(struct e1000_hw *hw) |
225 | { |
226 | u32 eecd; |
227 | |
228 | e1000_stop_nvm(hw); |
229 | |
230 | eecd = er32(EECD); |
231 | eecd &= ~E1000_EECD_REQ; |
232 | ew32(EECD, eecd); |
233 | } |
234 | |
235 | /** |
236 | * e1000_ready_nvm_eeprom - Prepares EEPROM for read/write |
237 | * @hw: pointer to the HW structure |
238 | * |
239 | * Setups the EEPROM for reading and writing. |
240 | **/ |
241 | static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw) |
242 | { |
243 | struct e1000_nvm_info *nvm = &hw->nvm; |
244 | u32 eecd = er32(EECD); |
245 | u8 spi_stat_reg; |
246 | |
247 | if (nvm->type == e1000_nvm_eeprom_spi) { |
248 | u16 timeout = NVM_MAX_RETRY_SPI; |
249 | |
250 | /* Clear SK and CS */ |
251 | eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); |
252 | ew32(EECD, eecd); |
253 | e1e_flush(); |
254 | udelay(1); |
255 | |
256 | /* Read "Status Register" repeatedly until the LSB is cleared. |
257 | * The EEPROM will signal that the command has been completed |
258 | * by clearing bit 0 of the internal status register. If it's |
259 | * not cleared within 'timeout', then error out. |
260 | */ |
261 | while (timeout) { |
262 | e1000_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI, |
263 | count: hw->nvm.opcode_bits); |
264 | spi_stat_reg = (u8)e1000_shift_in_eec_bits(hw, count: 8); |
265 | if (!(spi_stat_reg & NVM_STATUS_RDY_SPI)) |
266 | break; |
267 | |
268 | udelay(5); |
269 | e1000_standby_nvm(hw); |
270 | timeout--; |
271 | } |
272 | |
273 | if (!timeout) { |
274 | e_dbg("SPI NVM Status error\n" ); |
275 | return -E1000_ERR_NVM; |
276 | } |
277 | } |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | /** |
283 | * e1000e_read_nvm_eerd - Reads EEPROM using EERD register |
284 | * @hw: pointer to the HW structure |
285 | * @offset: offset of word in the EEPROM to read |
286 | * @words: number of words to read |
287 | * @data: word read from the EEPROM |
288 | * |
289 | * Reads a 16 bit word from the EEPROM using the EERD register. |
290 | **/ |
291 | s32 e1000e_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) |
292 | { |
293 | struct e1000_nvm_info *nvm = &hw->nvm; |
294 | u32 i, eerd = 0; |
295 | s32 ret_val = 0; |
296 | |
297 | /* A check for invalid values: offset too large, too many words, |
298 | * too many words for the offset, and not enough words. |
299 | */ |
300 | if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || |
301 | (words == 0)) { |
302 | e_dbg("nvm parameter(s) out of bounds\n" ); |
303 | return -E1000_ERR_NVM; |
304 | } |
305 | |
306 | for (i = 0; i < words; i++) { |
307 | eerd = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) + |
308 | E1000_NVM_RW_REG_START; |
309 | |
310 | ew32(EERD, eerd); |
311 | ret_val = e1000e_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ); |
312 | if (ret_val) { |
313 | e_dbg("NVM read error: %d\n" , ret_val); |
314 | break; |
315 | } |
316 | |
317 | data[i] = (er32(EERD) >> E1000_NVM_RW_REG_DATA); |
318 | } |
319 | |
320 | return ret_val; |
321 | } |
322 | |
323 | /** |
324 | * e1000e_write_nvm_spi - Write to EEPROM using SPI |
325 | * @hw: pointer to the HW structure |
326 | * @offset: offset within the EEPROM to be written to |
327 | * @words: number of words to write |
328 | * @data: 16 bit word(s) to be written to the EEPROM |
329 | * |
330 | * Writes data to EEPROM at offset using SPI interface. |
331 | * |
332 | * If e1000e_update_nvm_checksum is not called after this function , the |
333 | * EEPROM will most likely contain an invalid checksum. |
334 | **/ |
335 | s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) |
336 | { |
337 | struct e1000_nvm_info *nvm = &hw->nvm; |
338 | s32 ret_val = -E1000_ERR_NVM; |
339 | u16 widx = 0; |
340 | |
341 | /* A check for invalid values: offset too large, too many words, |
342 | * and not enough words. |
343 | */ |
344 | if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || |
345 | (words == 0)) { |
346 | e_dbg("nvm parameter(s) out of bounds\n" ); |
347 | return -E1000_ERR_NVM; |
348 | } |
349 | |
350 | while (widx < words) { |
351 | u8 write_opcode = NVM_WRITE_OPCODE_SPI; |
352 | |
353 | ret_val = nvm->ops.acquire(hw); |
354 | if (ret_val) |
355 | return ret_val; |
356 | |
357 | ret_val = e1000_ready_nvm_eeprom(hw); |
358 | if (ret_val) { |
359 | nvm->ops.release(hw); |
360 | return ret_val; |
361 | } |
362 | |
363 | e1000_standby_nvm(hw); |
364 | |
365 | /* Send the WRITE ENABLE command (8 bit opcode) */ |
366 | e1000_shift_out_eec_bits(hw, NVM_WREN_OPCODE_SPI, |
367 | count: nvm->opcode_bits); |
368 | |
369 | e1000_standby_nvm(hw); |
370 | |
371 | /* Some SPI eeproms use the 8th address bit embedded in the |
372 | * opcode |
373 | */ |
374 | if ((nvm->address_bits == 8) && (offset >= 128)) |
375 | write_opcode |= NVM_A8_OPCODE_SPI; |
376 | |
377 | /* Send the Write command (8-bit opcode + addr) */ |
378 | e1000_shift_out_eec_bits(hw, data: write_opcode, count: nvm->opcode_bits); |
379 | e1000_shift_out_eec_bits(hw, data: (u16)((offset + widx) * 2), |
380 | count: nvm->address_bits); |
381 | |
382 | /* Loop to allow for up to whole page write of eeprom */ |
383 | while (widx < words) { |
384 | u16 word_out = data[widx]; |
385 | |
386 | word_out = (word_out >> 8) | (word_out << 8); |
387 | e1000_shift_out_eec_bits(hw, data: word_out, count: 16); |
388 | widx++; |
389 | |
390 | if ((((offset + widx) * 2) % nvm->page_size) == 0) { |
391 | e1000_standby_nvm(hw); |
392 | break; |
393 | } |
394 | } |
395 | usleep_range(min: 10000, max: 11000); |
396 | nvm->ops.release(hw); |
397 | } |
398 | |
399 | return ret_val; |
400 | } |
401 | |
402 | /** |
403 | * e1000_read_pba_string_generic - Read device part number |
404 | * @hw: pointer to the HW structure |
405 | * @pba_num: pointer to device part number |
406 | * @pba_num_size: size of part number buffer |
407 | * |
408 | * Reads the product board assembly (PBA) number from the EEPROM and stores |
409 | * the value in pba_num. |
410 | **/ |
411 | s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num, |
412 | u32 pba_num_size) |
413 | { |
414 | s32 ret_val; |
415 | u16 nvm_data; |
416 | u16 pba_ptr; |
417 | u16 offset; |
418 | u16 length; |
419 | |
420 | if (pba_num == NULL) { |
421 | e_dbg("PBA string buffer was null\n" ); |
422 | return -E1000_ERR_INVALID_ARGUMENT; |
423 | } |
424 | |
425 | ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_0, words: 1, data: &nvm_data); |
426 | if (ret_val) { |
427 | e_dbg("NVM Read Error\n" ); |
428 | return ret_val; |
429 | } |
430 | |
431 | ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_1, words: 1, data: &pba_ptr); |
432 | if (ret_val) { |
433 | e_dbg("NVM Read Error\n" ); |
434 | return ret_val; |
435 | } |
436 | |
437 | /* if nvm_data is not ptr guard the PBA must be in legacy format which |
438 | * means pba_ptr is actually our second data word for the PBA number |
439 | * and we can decode it into an ascii string |
440 | */ |
441 | if (nvm_data != NVM_PBA_PTR_GUARD) { |
442 | e_dbg("NVM PBA number is not stored as string\n" ); |
443 | |
444 | /* make sure callers buffer is big enough to store the PBA */ |
445 | if (pba_num_size < E1000_PBANUM_LENGTH) { |
446 | e_dbg("PBA string buffer too small\n" ); |
447 | return E1000_ERR_NO_SPACE; |
448 | } |
449 | |
450 | /* extract hex string from data and pba_ptr */ |
451 | pba_num[0] = (nvm_data >> 12) & 0xF; |
452 | pba_num[1] = (nvm_data >> 8) & 0xF; |
453 | pba_num[2] = (nvm_data >> 4) & 0xF; |
454 | pba_num[3] = nvm_data & 0xF; |
455 | pba_num[4] = (pba_ptr >> 12) & 0xF; |
456 | pba_num[5] = (pba_ptr >> 8) & 0xF; |
457 | pba_num[6] = '-'; |
458 | pba_num[7] = 0; |
459 | pba_num[8] = (pba_ptr >> 4) & 0xF; |
460 | pba_num[9] = pba_ptr & 0xF; |
461 | |
462 | /* put a null character on the end of our string */ |
463 | pba_num[10] = '\0'; |
464 | |
465 | /* switch all the data but the '-' to hex char */ |
466 | for (offset = 0; offset < 10; offset++) { |
467 | if (pba_num[offset] < 0xA) |
468 | pba_num[offset] += '0'; |
469 | else if (pba_num[offset] < 0x10) |
470 | pba_num[offset] += 'A' - 0xA; |
471 | } |
472 | |
473 | return 0; |
474 | } |
475 | |
476 | ret_val = e1000_read_nvm(hw, offset: pba_ptr, words: 1, data: &length); |
477 | if (ret_val) { |
478 | e_dbg("NVM Read Error\n" ); |
479 | return ret_val; |
480 | } |
481 | |
482 | if (length == 0xFFFF || length == 0) { |
483 | e_dbg("NVM PBA number section invalid length\n" ); |
484 | return -E1000_ERR_NVM_PBA_SECTION; |
485 | } |
486 | /* check if pba_num buffer is big enough */ |
487 | if (pba_num_size < (((u32)length * 2) - 1)) { |
488 | e_dbg("PBA string buffer too small\n" ); |
489 | return -E1000_ERR_NO_SPACE; |
490 | } |
491 | |
492 | /* trim pba length from start of string */ |
493 | pba_ptr++; |
494 | length--; |
495 | |
496 | for (offset = 0; offset < length; offset++) { |
497 | ret_val = e1000_read_nvm(hw, offset: pba_ptr + offset, words: 1, data: &nvm_data); |
498 | if (ret_val) { |
499 | e_dbg("NVM Read Error\n" ); |
500 | return ret_val; |
501 | } |
502 | pba_num[offset * 2] = (u8)(nvm_data >> 8); |
503 | pba_num[(offset * 2) + 1] = (u8)(nvm_data & 0xFF); |
504 | } |
505 | pba_num[offset * 2] = '\0'; |
506 | |
507 | return 0; |
508 | } |
509 | |
510 | /** |
511 | * e1000_read_mac_addr_generic - Read device MAC address |
512 | * @hw: pointer to the HW structure |
513 | * |
514 | * Reads the device MAC address from the EEPROM and stores the value. |
515 | * Since devices with two ports use the same EEPROM, we increment the |
516 | * last bit in the MAC address for the second port. |
517 | **/ |
518 | s32 e1000_read_mac_addr_generic(struct e1000_hw *hw) |
519 | { |
520 | u32 rar_high; |
521 | u32 rar_low; |
522 | u16 i; |
523 | |
524 | rar_high = er32(RAH(0)); |
525 | rar_low = er32(RAL(0)); |
526 | |
527 | for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++) |
528 | hw->mac.perm_addr[i] = (u8)(rar_low >> (i * 8)); |
529 | |
530 | for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++) |
531 | hw->mac.perm_addr[i + 4] = (u8)(rar_high >> (i * 8)); |
532 | |
533 | for (i = 0; i < ETH_ALEN; i++) |
534 | hw->mac.addr[i] = hw->mac.perm_addr[i]; |
535 | |
536 | return 0; |
537 | } |
538 | |
539 | /** |
540 | * e1000e_validate_nvm_checksum_generic - Validate EEPROM checksum |
541 | * @hw: pointer to the HW structure |
542 | * |
543 | * Calculates the EEPROM checksum by reading/adding each word of the EEPROM |
544 | * and then verifies that the sum of the EEPROM is equal to 0xBABA. |
545 | **/ |
546 | s32 e1000e_validate_nvm_checksum_generic(struct e1000_hw *hw) |
547 | { |
548 | s32 ret_val; |
549 | u16 checksum = 0; |
550 | u16 i, nvm_data; |
551 | |
552 | for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { |
553 | ret_val = e1000_read_nvm(hw, offset: i, words: 1, data: &nvm_data); |
554 | if (ret_val) { |
555 | e_dbg("NVM Read Error\n" ); |
556 | return ret_val; |
557 | } |
558 | checksum += nvm_data; |
559 | } |
560 | |
561 | if (checksum != (u16)NVM_SUM) { |
562 | e_dbg("NVM Checksum Invalid\n" ); |
563 | return -E1000_ERR_NVM; |
564 | } |
565 | |
566 | return 0; |
567 | } |
568 | |
569 | /** |
570 | * e1000e_update_nvm_checksum_generic - Update EEPROM checksum |
571 | * @hw: pointer to the HW structure |
572 | * |
573 | * Updates the EEPROM checksum by reading/adding each word of the EEPROM |
574 | * up to the checksum. Then calculates the EEPROM checksum and writes the |
575 | * value to the EEPROM. |
576 | **/ |
577 | s32 e1000e_update_nvm_checksum_generic(struct e1000_hw *hw) |
578 | { |
579 | s32 ret_val; |
580 | u16 checksum = 0; |
581 | u16 i, nvm_data; |
582 | |
583 | for (i = 0; i < NVM_CHECKSUM_REG; i++) { |
584 | ret_val = e1000_read_nvm(hw, offset: i, words: 1, data: &nvm_data); |
585 | if (ret_val) { |
586 | e_dbg("NVM Read Error while updating checksum.\n" ); |
587 | return ret_val; |
588 | } |
589 | checksum += nvm_data; |
590 | } |
591 | checksum = (u16)NVM_SUM - checksum; |
592 | ret_val = e1000_write_nvm(hw, NVM_CHECKSUM_REG, words: 1, data: &checksum); |
593 | if (ret_val) |
594 | e_dbg("NVM Write Error while updating checksum.\n" ); |
595 | |
596 | return ret_val; |
597 | } |
598 | |
599 | /** |
600 | * e1000e_reload_nvm_generic - Reloads EEPROM |
601 | * @hw: pointer to the HW structure |
602 | * |
603 | * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the |
604 | * extended control register. |
605 | **/ |
606 | void e1000e_reload_nvm_generic(struct e1000_hw *hw) |
607 | { |
608 | u32 ctrl_ext; |
609 | |
610 | usleep_range(min: 10, max: 20); |
611 | ctrl_ext = er32(CTRL_EXT); |
612 | ctrl_ext |= E1000_CTRL_EXT_EE_RST; |
613 | ew32(CTRL_EXT, ctrl_ext); |
614 | e1e_flush(); |
615 | } |
616 | |