VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevE1000Phy.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.5 KB
Line 
1/** $Id: DevE1000Phy.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DevE1000Phy - Intel 82540EM Ethernet Controller Internal PHY Emulation.
4 *
5 * Implemented in accordance with the specification:
6 * PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's
7 * Manual 82540EP/EM, 82541xx, 82544GC/EI, 82545GM/EM, 82546GB/EB, and
8 * 82547xx
9 *
10 * 317453-002 Revision 3.5
11 */
12
13/*
14 * Copyright (C) 2007-2022 Oracle and/or its affiliates.
15 *
16 * This file is part of VirtualBox base platform packages, as
17 * available from https://www.virtualbox.org.
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation, in version 3 of the
22 * License.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, see <https://www.gnu.org/licenses>.
31 *
32 * SPDX-License-Identifier: GPL-3.0-only
33 */
34
35#define LOG_GROUP LOG_GROUP_DEV_E1000
36
37/** @todo Remove me! For now I want asserts to work in release code. */
38// #ifndef RT_STRICT
39// #define RT_STRICT
40#include <iprt/assert.h>
41// #undef RT_STRICT
42// #endif
43
44#include <iprt/errcore.h>
45#include <VBox/log.h>
46#ifdef IN_RING3
47# include <VBox/vmm/pdmdev.h>
48#endif
49#include "DevE1000Phy.h"
50
51/* Little helpers ************************************************************/
52#ifdef PHY_UNIT_TEST
53# define SSMR3PutMem(a,b,c)
54# define SSMR3GetMem(a,b,c)
55# include <stdio.h>
56# define PhyLog(a) printf a
57#else /* PHY_UNIT_TEST */
58# define PhyLog(a) Log(a)
59#endif /* PHY_UNIT_TEST */
60
61#define REG(x) pPhy->au16Regs[x##_IDX]
62
63
64/* Internals */
65namespace Phy {
66#if defined(LOG_ENABLED) && !defined(PHY_UNIT_TEST)
67 /** Retrieves state name by id */
68 static const char * getStateName(uint16_t u16State);
69#endif
70 /** Look up register index by address. */
71 static int lookupRegister(uint32_t u32Address);
72 /** Software-triggered reset. */
73 static void softReset(PPHY pPhy, PPDMDEVINS pDevIns);
74
75 /** Read callback. */
76 typedef uint16_t FNREAD(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns);
77 /** Write callback. */
78 typedef void FNWRITE(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns);
79
80 /** @name Generic handlers
81 * @{ */
82 static FNREAD regReadDefault;
83 static FNWRITE regWriteDefault;
84 static FNREAD regReadForbidden;
85 static FNWRITE regWriteForbidden;
86 static FNREAD regReadUnimplemented;
87 static FNWRITE regWriteUnimplemented;
88 /** @} */
89 /** @name Register-specific handlers
90 * @{ */
91 static FNWRITE regWritePCTRL;
92 static FNREAD regReadPSTATUS;
93 static FNREAD regReadGSTATUS;
94 /** @} */
95
96 /**
97 * PHY register map table.
98 *
99 * Override pfnRead and pfnWrite to implement register-specific behavior.
100 */
101 static struct RegMap_st
102 {
103 /** PHY register address. */
104 uint32_t u32Address;
105 /** Read callback. */
106 FNREAD *pfnRead;
107 /** Write callback. */
108 FNWRITE *pfnWrite;
109 /** Abbreviated name. */
110 const char *pszAbbrev;
111 /** Full name. */
112 const char *pszName;
113 } s_regMap[NUM_OF_PHY_REGS] =
114 {
115 /*ra read callback write callback abbrev full name */
116 /*-- ------------------------- -------------------------- ---------- ------------------------------*/
117 { 0, Phy::regReadDefault , Phy::regWritePCTRL , "PCTRL" , "PHY Control" },
118 { 1, Phy::regReadPSTATUS , Phy::regWriteForbidden , "PSTATUS" , "PHY Status" },
119 { 2, Phy::regReadDefault , Phy::regWriteForbidden , "PID" , "PHY Identifier" },
120 { 3, Phy::regReadDefault , Phy::regWriteForbidden , "EPID" , "Extended PHY Identifier" },
121 { 4, Phy::regReadDefault , Phy::regWriteDefault , "ANA" , "Auto-Negotiation Advertisement" },
122 { 5, Phy::regReadDefault , Phy::regWriteForbidden , "LPA" , "Link Partner Ability" },
123 { 6, Phy::regReadUnimplemented, Phy::regWriteForbidden , "ANE" , "Auto-Negotiation Expansion" },
124 { 7, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "NPT" , "Next Page Transmit" },
125 { 8, Phy::regReadUnimplemented, Phy::regWriteForbidden , "LPN" , "Link Partner Next Page" },
126 { 9, Phy::regReadDefault , Phy::regWriteUnimplemented, "GCON" , "1000BASE-T Control" },
127 { 10, Phy::regReadGSTATUS , Phy::regWriteForbidden , "GSTATUS" , "1000BASE-T Status" },
128 { 15, Phy::regReadUnimplemented, Phy::regWriteForbidden , "EPSTATUS" , "Extended PHY Status" },
129 { 16, Phy::regReadDefault , Phy::regWriteDefault , "PSCON" , "PHY Specific Control" },
130 { 17, Phy::regReadDefault , Phy::regWriteForbidden , "PSSTAT" , "PHY Specific Status" },
131 { 18, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "PINTE" , "PHY Interrupt Enable" },
132 { 19, Phy::regReadUnimplemented, Phy::regWriteForbidden , "PINTS" , "PHY Interrupt Status" },
133 { 20, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "EPSCON1" , "Extended PHY Specific Control 1" },
134 { 21, Phy::regReadUnimplemented, Phy::regWriteForbidden , "PREC" , "PHY Receive Error Counter" },
135 { 26, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "EPSCON2" , "Extended PHY Specific Control 2" },
136 { 29, Phy::regReadForbidden , Phy::regWriteUnimplemented, "R30PS" , "MDI Register 30 Page Select" },
137 { 30, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "R30AW" , "MDI Register 30 Access Window" }
138 };
139}
140
141/**
142 * Default read handler.
143 *
144 * Fetches register value from the state structure.
145 *
146 * @returns Register value
147 *
148 * @param index Register index in register array.
149 */
150static uint16_t Phy::regReadDefault(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
151{
152 RT_NOREF(pDevIns);
153 AssertReturn(index<Phy::NUM_OF_PHY_REGS, 0);
154 return pPhy->au16Regs[index];
155}
156
157/**
158 * Default write handler.
159 *
160 * Writes the specified register value to the state structure.
161 *
162 * @param index Register index in register array.
163 * @param value The value to store (ignored).
164 */
165static void Phy::regWriteDefault(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
166{
167 RT_NOREF(pDevIns);
168 AssertReturnVoid(index < NUM_OF_PHY_REGS);
169 pPhy->au16Regs[index] = u16Value;
170}
171
172/**
173 * Read handler for write-only registers.
174 *
175 * Merely reports reads from write-only registers.
176 *
177 * @returns Register value (always 0)
178 *
179 * @param index Register index in register array.
180 */
181static uint16_t Phy::regReadForbidden(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
182{
183 RT_NOREF(pPhy, index, pDevIns);
184 PhyLog(("PHY#%d At %02d read attempted from write-only '%s'\n",
185 pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
186 return 0;
187}
188
189/**
190 * Write handler for read-only registers.
191 *
192 * Merely reports writes to read-only registers.
193 *
194 * @param index Register index in register array.
195 * @param value The value to store (ignored).
196 */
197static void Phy::regWriteForbidden(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
198{
199 RT_NOREF(pPhy, index, u16Value, pDevIns);
200 PhyLog(("PHY#%d At %02d write attempted to read-only '%s'\n",
201 pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
202}
203
204/**
205 * Read handler for unimplemented registers.
206 *
207 * Merely reports reads from unimplemented registers.
208 *
209 * @returns Register value (always 0)
210 *
211 * @param index Register index in register array.
212 */
213static uint16_t Phy::regReadUnimplemented(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
214{
215 RT_NOREF(pPhy, index, pDevIns);
216 PhyLog(("PHY#%d At %02d read attempted from unimplemented '%s'\n",
217 pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
218 return 0;
219}
220
221/**
222 * Write handler for unimplemented registers.
223 *
224 * Merely reports writes to unimplemented registers.
225 *
226 * @param index Register index in register array.
227 * @param value The value to store (ignored).
228 */
229static void Phy::regWriteUnimplemented(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
230{
231 RT_NOREF(pPhy, index, u16Value, pDevIns);
232 PhyLog(("PHY#%d At %02d write attempted to unimplemented '%s'\n",
233 pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
234}
235
236
237/**
238 * Search PHY register table for register with matching address.
239 *
240 * @returns Index in the register table or -1 if not found.
241 *
242 * @param u32Address Register address.
243 */
244static int Phy::lookupRegister(uint32_t u32Address)
245{
246 unsigned int index;
247
248 for (index = 0; index < RT_ELEMENTS(s_regMap); index++)
249 {
250 if (s_regMap[index].u32Address == u32Address)
251 {
252 return (int)index;
253 }
254 }
255
256 return -1;
257}
258
259/**
260 * Read PHY register.
261 *
262 * @returns Value of specified PHY register.
263 *
264 * @param u32Address Register address.
265 */
266uint16_t Phy::readRegister(PPHY pPhy, uint32_t u32Address, PPDMDEVINS pDevIns)
267{
268 int index = Phy::lookupRegister(u32Address);
269 uint16_t u16 = 0;
270
271 if (index >= 0)
272 {
273 u16 = s_regMap[index].pfnRead(pPhy, (uint32_t)index, pDevIns);
274 PhyLog(("PHY#%d At %02d read %04X from %s (%s)\n",
275 pPhy->iInstance, s_regMap[index].u32Address, u16,
276 s_regMap[index].pszAbbrev, s_regMap[index].pszName));
277 }
278 else
279 {
280 PhyLog(("PHY#%d read attempted from non-existing register %08x\n",
281 pPhy->iInstance, u32Address));
282 }
283 return u16;
284}
285
286/**
287 * Write to PHY register.
288 *
289 * @param u32Address Register address.
290 * @param u16Value Value to store.
291 */
292void Phy::writeRegister(PPHY pPhy, uint32_t u32Address, uint16_t u16Value, PPDMDEVINS pDevIns)
293{
294 int index = Phy::lookupRegister(u32Address);
295
296 if (index >= 0)
297 {
298 PhyLog(("PHY#%d At %02d write %04X to %s (%s)\n",
299 pPhy->iInstance, s_regMap[index].u32Address, u16Value,
300 s_regMap[index].pszAbbrev, s_regMap[index].pszName));
301 s_regMap[index].pfnWrite(pPhy, (uint32_t)index, u16Value, pDevIns);
302 }
303 else
304 {
305 PhyLog(("PHY#%d write attempted to non-existing register %08x\n",
306 pPhy->iInstance, u32Address));
307 }
308}
309
310/**
311 * PHY constructor.
312 *
313 * Stores E1000 instance internally. Triggers PHY hard reset.
314 *
315 * @param iNICInstance Number of network controller instance this PHY is
316 * attached to.
317 * @param u16EPid Extended PHY Id.
318 */
319void Phy::init(PPHY pPhy, int iNICInstance, uint16_t u16EPid)
320{
321 pPhy->iInstance = iNICInstance;
322 /* The PHY identifier composed of bits 3 through 18 of the OUI */
323 /* (Organizationally Unique Identifier). OUI is 0x05043. */
324 REG(PID) = 0x0141;
325 /* Extended PHY identifier */
326 REG(EPID) = u16EPid;
327 hardReset(pPhy);
328}
329
330/**
331 * Hardware PHY reset.
332 *
333 * Sets all PHY registers to their initial values.
334 */
335void Phy::hardReset(PPHY pPhy)
336{
337 PhyLog(("PHY#%d Hard reset\n", pPhy->iInstance));
338 REG(PCTRL) = PCTRL_SPDSELM | PCTRL_DUPMOD | PCTRL_ANEG;
339 /*
340 * 100 and 10 FD/HD, Extended Status, MF Preamble Suppression,
341 * AUTO NEG AB, EXT CAP
342 */
343 REG(PSTATUS) = 0x7949;
344 REG(ANA) = 0x01E1;
345 /* No flow control by our link partner, all speeds */
346 REG(LPA) = 0x01E0;
347 REG(ANE) = 0x0000;
348 REG(NPT) = 0x2001;
349 REG(LPN) = 0x0000;
350 REG(GCON) = 0x1E00;
351 REG(GSTATUS) = 0x0000;
352 REG(EPSTATUS) = 0x3000;
353 REG(PSCON) = 0x0068;
354 REG(PSSTAT) = 0x0000;
355 REG(PINTE) = 0x0000;
356 REG(PINTS) = 0x0000;
357 REG(EPSCON1) = 0x0D60;
358 REG(PREC) = 0x0000;
359 REG(EPSCON2) = 0x000C;
360 REG(R30PS) = 0x0000;
361 REG(R30AW) = 0x0000;
362
363 pPhy->u16State = MDIO_IDLE;
364}
365
366/**
367 * Software PHY reset.
368 */
369static void Phy::softReset(PPHY pPhy, PPDMDEVINS pDevIns)
370{
371 PhyLog(("PHY#%d Soft reset\n", pPhy->iInstance));
372
373 REG(PCTRL) = REG(PCTRL) & (PCTRL_SPDSELM | PCTRL_DUPMOD | PCTRL_ANEG | PCTRL_SPDSELL);
374 /*
375 * 100 and 10 FD/HD, Extended Status, MF Preamble Suppression,
376 * AUTO NEG AB, EXT CAP
377 */
378 REG(PSTATUS) = 0x7949;
379 REG(PSSTAT) &= 0xe001;
380 PhyLog(("PHY#%d PSTATUS=%04x PSSTAT=%04x\n", pPhy->iInstance, REG(PSTATUS), REG(PSSTAT)));
381
382 e1kPhyLinkResetCallback(pDevIns);
383}
384
385/**
386 * Get the current state of the link.
387 *
388 * @returns true if link is up.
389 */
390bool Phy::isLinkUp(PPHY pPhy)
391{
392 return (REG(PSSTAT) & PSSTAT_LINK) != 0;
393}
394
395/**
396 * Set the current state of the link.
397 *
398 * @remarks Link Status bit in PHY Status register is latched-low and does
399 * not change the state when the link goes up.
400 *
401 * @param fLinkIsUp New state of the link.
402 */
403void Phy::setLinkStatus(PPHY pPhy, bool fLinkIsUp)
404{
405 if (fLinkIsUp)
406 {
407 REG(PSSTAT) |= PSSTAT_LINK_ALL;
408 REG(PSTATUS) |= PSTATUS_NEGCOMP; /* PSTATUS_LNKSTAT is latched low */
409 }
410 else
411 {
412 REG(PSSTAT) &= ~PSSTAT_LINK_ALL;
413 REG(PSTATUS) &= ~(PSTATUS_LNKSTAT | PSTATUS_NEGCOMP);
414 }
415 PhyLog(("PHY#%d setLinkStatus: PSTATUS=%04x PSSTAT=%04x\n", pPhy->iInstance, REG(PSTATUS), REG(PSSTAT)));
416}
417
418#ifdef IN_RING3
419
420/**
421 * Save PHY state.
422 *
423 * @remarks Since PHY is aggregated into E1K it does not currently supports
424 * versioning of its own.
425 *
426 * @returns VBox status code.
427 * @param pHlp Device helper table.
428 * @param pSSM The handle to save the state to.
429 * @param pPhy The pointer to this instance.
430 */
431int Phy::saveState(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPHY pPhy)
432{
433 pHlp->pfnSSMPutMem(pSSM, pPhy->au16Regs, sizeof(pPhy->au16Regs));
434 return VINF_SUCCESS;
435}
436
437/**
438 * Restore previously saved PHY state.
439 *
440 * @remarks Since PHY is aggregated into E1K it does not currently supports
441 * versioning of its own.
442 *
443 * @returns VBox status code.
444 * @param pHlp Device helper table.
445 * @param pSSM The handle to save the state to.
446 * @param pPhy The pointer to this instance.
447 */
448int Phy::loadState(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPHY pPhy)
449{
450 return pHlp->pfnSSMGetMem(pSSM, pPhy->au16Regs, sizeof(pPhy->au16Regs));
451}
452
453#endif /* IN_RING3 */
454
455/* Register-specific handlers ************************************************/
456
457/**
458 * Write handler for PHY Control register.
459 *
460 * Handles reset.
461 *
462 * @param index Register index in register array.
463 * @param value The value to store (ignored).
464 */
465static void Phy::regWritePCTRL(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
466{
467 if (u16Value & PCTRL_RESET)
468 softReset(pPhy, pDevIns);
469 else
470 regWriteDefault(pPhy, index, u16Value, pDevIns);
471}
472
473/**
474 * Read handler for PHY Status register.
475 *
476 * Handles Latched-Low Link Status bit.
477 *
478 * @returns Register value
479 *
480 * @param index Register index in register array.
481 */
482static uint16_t Phy::regReadPSTATUS(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
483{
484 RT_NOREF(pPhy, index, pDevIns);
485
486 /* Read latched value */
487 uint16_t u16 = REG(PSTATUS);
488 if (REG(PSSTAT) & PSSTAT_LINK)
489 REG(PSTATUS) |= PSTATUS_LNKSTAT;
490 else
491 REG(PSTATUS) &= ~PSTATUS_LNKSTAT;
492 return u16;
493}
494
495/**
496 * Read handler for 1000BASE-T Status register.
497 *
498 * @returns Register value
499 *
500 * @param index Register index in register array.
501 */
502static uint16_t Phy::regReadGSTATUS(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
503{
504 RT_NOREF(pPhy, index, pDevIns);
505
506 /*
507 * - Link partner is capable of 1000BASE-T half duplex
508 * - Link partner is capable of 1000BASE-T full duplex
509 * - Remote receiver OK
510 * - Local receiver OK
511 * - Local PHY config resolved to SLAVE
512 */
513 return 0x3C00;
514}
515
516#if defined(LOG_ENABLED) && !defined(PHY_UNIT_TEST)
517static const char * Phy::getStateName(uint16_t u16State)
518{
519 static const char *pcszState[] =
520 {
521 "MDIO_IDLE",
522 "MDIO_ST",
523 "MDIO_OP_ADR",
524 "MDIO_TA_RD",
525 "MDIO_TA_WR",
526 "MDIO_READ",
527 "MDIO_WRITE"
528 };
529
530 return (u16State < RT_ELEMENTS(pcszState)) ? pcszState[u16State] : "<invalid>";
531}
532#endif
533
534bool Phy::readMDIO(PPHY pPhy)
535{
536 bool fPin = false;
537
538 switch (pPhy->u16State)
539 {
540 case MDIO_TA_RD:
541 Assert(pPhy->u16Cnt == 1);
542 fPin = false;
543 pPhy->u16State = MDIO_READ;
544 pPhy->u16Cnt = 16;
545 break;
546 case MDIO_READ:
547 /* Bits are shifted out in MSB to LSB order */
548 fPin = (pPhy->u16Acc & 0x8000) != 0;
549 pPhy->u16Acc <<= 1;
550 if (--pPhy->u16Cnt == 0)
551 pPhy->u16State = MDIO_IDLE;
552 break;
553 default:
554 PhyLog(("PHY#%d WARNING! MDIO pin read in %s state\n", pPhy->iInstance, Phy::getStateName(pPhy->u16State)));
555 pPhy->u16State = MDIO_IDLE;
556 }
557 return fPin;
558}
559
560/** Set the value of MDIO pin. */
561void Phy::writeMDIO(PPHY pPhy, bool fPin, PPDMDEVINS pDevIns)
562{
563 switch (pPhy->u16State)
564 {
565 case MDIO_IDLE:
566 if (!fPin)
567 pPhy->u16State = MDIO_ST;
568 break;
569 case MDIO_ST:
570 if (fPin)
571 {
572 pPhy->u16State = MDIO_OP_ADR;
573 pPhy->u16Cnt = 12; /* OP + PHYADR + REGADR */
574 pPhy->u16Acc = 0;
575 }
576 break;
577 case MDIO_OP_ADR:
578 Assert(pPhy->u16Cnt);
579 /* Shift in 'u16Cnt' bits into accumulator */
580 pPhy->u16Acc <<= 1;
581 if (fPin)
582 pPhy->u16Acc |= 1;
583 if (--pPhy->u16Cnt == 0)
584 {
585 /* Got OP(2) + PHYADR(5) + REGADR(5) */
586 /* Note: A single PHY is supported, ignore PHYADR */
587 switch (pPhy->u16Acc >> 10)
588 {
589 case MDIO_READ_OP:
590 pPhy->u16Acc = readRegister(pPhy, pPhy->u16Acc & 0x1F, pDevIns);
591 pPhy->u16State = MDIO_TA_RD;
592 pPhy->u16Cnt = 1;
593 break;
594 case MDIO_WRITE_OP:
595 pPhy->u16RegAdr = pPhy->u16Acc & 0x1F;
596 pPhy->u16State = MDIO_TA_WR;
597 pPhy->u16Cnt = 2;
598 break;
599 default:
600 PhyLog(("PHY#%d ERROR! Invalid MDIO op: %d\n", pPhy->iInstance, pPhy->u16Acc >> 10));
601 pPhy->u16State = MDIO_IDLE;
602 break;
603 }
604 }
605 break;
606 case MDIO_TA_WR:
607 Assert(pPhy->u16Cnt <= 2);
608 Assert(pPhy->u16Cnt > 0);
609 if (--pPhy->u16Cnt == 0)
610 {
611 pPhy->u16State = MDIO_WRITE;
612 pPhy->u16Cnt = 16;
613 }
614 break;
615 case MDIO_WRITE:
616 Assert(pPhy->u16Cnt);
617 pPhy->u16Acc <<= 1;
618 if (fPin)
619 pPhy->u16Acc |= 1;
620 if (--pPhy->u16Cnt == 0)
621 {
622 writeRegister(pPhy, pPhy->u16RegAdr, pPhy->u16Acc, pDevIns);
623 pPhy->u16State = MDIO_IDLE;
624 }
625 break;
626 default:
627 PhyLog(("PHY#%d ERROR! MDIO pin write in %s state\n", pPhy->iInstance, Phy::getStateName(pPhy->u16State)));
628 pPhy->u16State = MDIO_IDLE;
629 break;
630 }
631}
632
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use