VirtualBox

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

Last change on this file since 64925 was 64925, checked in by vboxsync, 8 years ago

Dev/E1000: (bugref:8624) Bring the link up in 2 seconds, PHY soft reset implemented partially.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette