VirtualBox

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

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

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