VirtualBox

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

Last change on this file since 22013 was 18438, checked in by vboxsync, 16 years ago

DevE1000Phy.cpp: Fixed MSC warning about converting 0x8000 to bool. (previous rev was accidentally mixed with PCNet, sorry.)

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