|
|
1.1 root 1: /*
2: * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * Copyright (c) 1996 NeXT Software, Inc. All rights reserved.
24: *
25: * i82557PHY.cpp
26: *
27: */
28:
29: #include "i82557.h"
30: #include "i82557PHY.h"
31:
32: //---------------------------------------------------------------------------
33: // Function: _logMDIStatus
34: //
35: // Purpose:
36: // Dump the contents of the MDI status register.
37:
38: static inline void
39: _logMDIStatus(mdi_reg_t reg)
40: {
41: if (reg & MDI_STATUS_T4)
42: IOLog("PHY: T4 capable\n");
43: if (reg & MDI_STATUS_TX_FD)
44: IOLog("PHY: 100Base-TX full duplex capable\n");
45: if (reg & MDI_STATUS_TX_HD)
46: IOLog("PHY: 100Base-TX half duplex capable\n");
47: if (reg & MDI_STATUS_10_FD)
48: IOLog("PHY: 10Base-T full duplex capable\n");
49: if (reg & MDI_STATUS_10_HD)
50: IOLog("PHY: 10Base-T half duplex capable\n");
51: if (reg & MDI_STATUS_EXTENDED_CAPABILITY)
52: IOLog("PHY: has extended capability registers\n");
53: if (reg & MDI_STATUS_JABBER_DETECTED)
54: IOLog("PHY: jabberDetect set\n");
55: if (reg & MDI_STATUS_AUTONEG_CAPABLE)
56: IOLog("PHY: auto negotiation capable\n");
57: IOLog("PHY: link is %s\n", (reg & MDI_STATUS_LINK_STATUS) ? "UP" : "DOWN");
58: return;
59: }
60:
61: //---------------------------------------------------------------------------
62: // Function: _getModelId
63: //
64: // Purpose:
65: // Read the MDI ID registers and form a single 32-bit id.
66:
67: UInt32 Intel82557::_phyGetID()
68: {
69: UInt16 id1, id2;
70: _mdiReadPHY(phyAddr, MDI_REG_PHYID_WORD_1, &id1);
71: _mdiReadPHY(phyAddr, MDI_REG_PHYID_WORD_2, &id2);
72: return ((id2 << 16) | id1);
73: }
74:
75: //---------------------------------------------------------------------------
76: // Function: _phySetMedium
77: //
78: // Purpose:
79: // Setup the PHY to the medium type given.
80: // Returns true on success.
81:
82: bool Intel82557::_phySetMedium(mediumType_t medium)
83: {
84: mdi_reg_t status;
85: mdi_reg_t control;
86: mediumType_t phyMedium = medium;
87: UInt32 mediumCapableMask;
88:
89: // Reset PHY before changing medium selection.
90: //
91: _phyReset();
92:
93: // Get local capability.
94: //
95: _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
96:
97: // Create a medium capable mask.
98: //
99: mediumCapableMask = (status >> 11) & 0x1f;
100:
101: // Force the PHY's data rate and duplex settings if the medium type
102: // chosen is not AUTO.
103: //
104: if (phyMedium != MEDIUM_TYPE_AUTO) {
105: if ((MEDIUM_TYPE_TO_MASK(phyMedium) & mediumCapableMask) == 0) {
106: // Hardware is not capable of selecting the user-selected
107: // medium.
108: //
109: return false;
110: }
111: else {
112: // Medium chosen is valid, go ahead and set PHY.
113: //
114: bool speed100 = false;
115: bool fullDuplex = false;
116:
117: if ((medium == MEDIUM_TYPE_TX_HD) ||
118: (medium == MEDIUM_TYPE_TX_FD) ||
119: (medium == MEDIUM_TYPE_T4))
120: speed100 = true;
121:
122: if ((medium == MEDIUM_TYPE_10_FD) || (medium == MEDIUM_TYPE_TX_FD))
123: fullDuplex = true;
124:
125: // Disable auto-negotiation function and force speed + duplex.
126: //
127: IOSleep(300);
128:
129: control = ((speed100 ? MDI_CONTROL_100 : 0) |
130: (fullDuplex ? MDI_CONTROL_FULL_DUPLEX : 0));
131:
132: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
133:
134: VPRINT("%s: user forced %s Mbit/s%s mode\n", getName(),
135: speed100 ? "100" : "10",
136: fullDuplex ? " full duplex" : "");
137:
138: IOSleep(50);
139: }
140: }
141: else {
142: // For MEDIUM_TYPE_AUTO, enable and restart auto-negotiation.
143: //
144: control = MDI_CONTROL_AUTONEG_ENABLE;
145: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
146: IOSleep(1);
147: control |= MDI_CONTROL_RESTART_AUTONEG;
148: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
149: }
150:
151: // Some special bit twiddling for NSC83840.
152: //
153: if (phyID == PHY_MODEL_NSC83840) {
154: /* set-up National Semiconductor 83840 specific registers */
155:
156: mdi_reg_t reg;
157:
158: VPRINT("%s: setting NSC83840-specific registers\n", getName());
159: _mdiReadPHY(phyAddr, NSC83840_REG_PCR, ®);
160:
161: /*
162: * This bit MUST be set, otherwise the card may not transmit at
163: * all in 100Mb/s mode. This is specially true for 82557 cards
164: * with the DP83840 PHY.
165: *
166: * In the NSC documentation, bit 10 of PCS register is labeled
167: * as a reserved bit. What is the real function of this bit?
168: */
169: reg |= (NSC83840_PCR_TXREADY | NSC83840_PCR_CIM_DIS);
170:
171: _mdiWritePHY(phyAddr, NSC83840_REG_PCR, reg);
172: }
173:
174: currentMediumType = medium;
175:
176: return true;
177: }
178:
179: //---------------------------------------------------------------------------
180: // Function: _phyAddMediumType
181: //
182: // Purpose:
183: // Add a single medium object to the medium dictionary.
184: // Also add the medium object to an array for fast lookup.
185:
186: bool Intel82557::_phyAddMediumType(UInt32 type, UInt32 speed, UInt32 code)
187: {
188: IONetworkMedium * medium;
189: bool ret = false;
190:
191: medium = IONetworkMedium::medium(type, speed, 0, code);
192: if (medium) {
193: ret = IONetworkMedium::addMedium(mediumDict, medium);
194: if (ret)
195: mediumTable[code] = medium;
196: medium->release();
197: }
198: return ret;
199: }
200:
201: //---------------------------------------------------------------------------
202: // Function: _phyPublishMedia
203: //
204: // Purpose:
205: // Examine the PHY capabilities and advertise all supported medium types.
206: //
207: // FIXME: Non PHY medium types are not probed.
208:
209: #define MBPS 1000000
210:
211: void Intel82557::_phyPublishMedia()
212: {
213: mdi_reg_t status;
214:
215: // Read the PHY's media capability.
216: //
217: _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
218:
219: _phyAddMediumType(kIOMediumEtherAuto,
220: 0,
221: MEDIUM_TYPE_AUTO);
222:
223: if (status & MDI_STATUS_10_HD)
224: _phyAddMediumType(kIOMediumEther10BaseT | kIOMediumHalfDuplex,
225: 10 * MBPS,
226: MEDIUM_TYPE_10_HD);
227:
228: if (status & MDI_STATUS_10_FD)
229: _phyAddMediumType(kIOMediumEther10BaseT | kIOMediumFullDuplex,
230: 10 * MBPS,
231: MEDIUM_TYPE_10_FD);
232:
233: if (status & MDI_STATUS_TX_HD)
234: _phyAddMediumType(kIOMediumEther100BaseTX | kIOMediumHalfDuplex,
235: 100 * MBPS,
236: MEDIUM_TYPE_TX_HD);
237:
238: if (status & MDI_STATUS_TX_FD)
239: _phyAddMediumType(kIOMediumEther100BaseTX | kIOMediumFullDuplex,
240: 100 * MBPS,
241: MEDIUM_TYPE_TX_FD);
242:
243: if (status & MDI_STATUS_T4)
244: _phyAddMediumType(kIOMediumEther100BaseT4,
245: 100 * MBPS,
246: MEDIUM_TYPE_T4);
247: }
248:
249: //---------------------------------------------------------------------------
250: // Function: _phyReset
251: //
252: // Purpose:
253: // Reset the PHY.
254:
255: #define PHY_RESET_TIMEOUT 100 // ms
256: #define PHY_RESET_DELAY 10 // ms
257: #define PHY_POST_RESET_DELAY 300 // us
258:
259: bool Intel82557::_phyReset()
260: {
261: int i = PHY_RESET_TIMEOUT;
262: mdi_reg_t control;
263:
264: if (!_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control))
265: return false;
266:
267: // Set the reset bit in the PHY Control register
268: //
269: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control | MDI_CONTROL_RESET);
270:
271: // Wait till reset process is complete (MDI_CONTROL_RESET returns to zero)
272: //
273: while (i > 0) {
274: if (!_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control))
275: return false;
276: if ((control & MDI_CONTROL_RESET) == 0) {
277: IODelay(PHY_POST_RESET_DELAY);
278: return true;
279: }
280: IOSleep(PHY_RESET_DELAY);
281: i -= PHY_RESET_DELAY;
282: }
283: return false;
284: }
285:
286: //---------------------------------------------------------------------------
287: // Function: _phyWaitAutoNegotiation
288: //
289: // Purpose:
290: // Wait until auto-negotiation is complete.
291:
292: #define PHY_NWAY_TIMEOUT 5000 // ms
293: #define PHY_NWAY_DELAY 20 // ms
294:
295: bool Intel82557::_phyWaitAutoNegotiation()
296: {
297: int i = PHY_NWAY_TIMEOUT;
298: mdi_reg_t status;
299:
300: while (i > 0) {
301: if (!_mdiReadPHY(phyAddr, MDI_REG_STATUS, &status))
302: return false;
303:
304: if (status & MDI_STATUS_AUTONEG_COMPLETE)
305: return true;
306:
307: IOSleep(PHY_NWAY_DELAY);
308: i -= PHY_NWAY_DELAY;
309: }
310: return false;
311: }
312:
313: //---------------------------------------------------------------------------
314: // Function: _phyProbe
315: //
316: // Purpose:
317: // Find out which PHY is active.
318: //
319: #define AUTONEGOTIATE_TIMEOUT 35
320:
321: bool Intel82557::_phyProbe()
322: {
323: bool foundPhy1 = false;
324: mdi_reg_t control;
325: mdi_reg_t status;
326:
327: if (phyAddr == PHY_ADDRESS_I82503) {
328: VPRINT("%s: overriding to use Intel 82503", getName());
329: return true;
330: }
331:
332: if (phyAddr > 0 && phyAddr < PHY_ADDRESS_MAX) {
333: VPRINT("%s: looking for Phy 1 at address %d\n", getName(), phyAddr);
334: _mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control);
335: _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status); // do it twice
336: _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
337: if (control == 0xffff || (status == 0 && control == 0))
338: {
339: VPRINT("%s: Phy 1 at address %d does not exist\n", getName(),
340: phyAddr);
341: }
342: else {
343: VPRINT("%s: Phy 1 at address %d exists\n", getName(), phyAddr);
344: foundPhy1 = true;
345: if (status & MDI_STATUS_LINK_STATUS) {
346: VPRINT("%s: found Phy 1 at address %d with link\n",
347: getName(), phyAddr);
348: return true; // use PHY1
349: }
350: }
351: }
352:
353: // PHY1 does not exist, or it does not have valid link.
354: // Try PHY0 at address 0.
355: //
356: _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_CONTROL, &control);
357: _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
358:
359: if (control == 0xffff || (status == 0 && control == 0)) {
360: if (phyAddr == 0) { /* if address forced to 0, then fail */
361: IOLog("%s: phy0 not detected\n", getName());
362: return false;
363: }
364: if (foundPhy1 == true) {
365: VPRINT("%s: no Phy at address 0, using Phy 1 without link\n",
366: getName());
367: return true; // use PHY1 without a valid link
368: }
369: VPRINT("%s: no Phy at address 0, defaulting to 82503\n", getName());
370: phyAddr = PHY_ADDRESS_I82503;
371: return true;
372: }
373:
374: // must isolate PHY1 electrically before using PHY0.
375: //
376: if (foundPhy1 == true) {
377: control = MDI_CONTROL_ISOLATE;
378: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
379: IOSleep(1);
380: }
381:
382: // Enable and restart auto-negotiation on PHY0.
383: //
384: VPRINT("%s: starting auto-negotiation on Phy 0", getName());
385: control = MDI_CONTROL_AUTONEG_ENABLE;
386: _mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
387: IOSleep(1);
388: control |= MDI_CONTROL_RESTART_AUTONEG;
389: _mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
390:
391: for (int i = 0; i < AUTONEGOTIATE_TIMEOUT; i++) {
392: _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
393: if (status & MDI_STATUS_AUTONEG_COMPLETE)
394: break;
395: IOSleep(100);
396: }
397: _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
398: _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
399: _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
400: if ((status & MDI_STATUS_LINK_STATUS) || foundPhy1 == false) {
401: VPRINT("%s: using Phy 0 at address 0\n", getName());
402: phyAddr = 0;
403: return true;
404: }
405:
406: // Isolate PHY0.
407: //
408: VPRINT("%s: using Phy 1 without link\n", getName());
409: control = MDI_CONTROL_ISOLATE;
410: _mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
411: IOSleep(1);
412:
413: // Enable and restart auto-negotiation on PHY1.
414: //
415: control = MDI_CONTROL_AUTONEG_ENABLE;
416: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
417: IOSleep(1);
418: control |= MDI_CONTROL_RESTART_AUTONEG;
419: _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
420:
421: phyID = _phyGetID();
422: VPRINT("%s: PHY model id is 0x%08lx\n", getName(), phyID);
423: phyID &= PHY_MODEL_MASK;
424:
425: return true;
426: }
427:
428: //---------------------------------------------------------------------------
429: // Function: _phyGetMediumTypeFromBits
430: //
431: // Purpose:
432: // Return the medium type that correspond to the given specifiers.
433:
434: mediumType_t Intel82557::_phyGetMediumTypeFromBits(bool rate100,
435: bool fullDuplex,
436: bool t4)
437: {
438: mediumType_t mediumType;
439:
440: if (t4) {
441: mediumType = MEDIUM_TYPE_T4;
442: }
443: else if (rate100) {
444: if (fullDuplex)
445: mediumType = MEDIUM_TYPE_TX_FD;
446: else
447: mediumType = MEDIUM_TYPE_TX_HD;
448: }
449: else {
450: if (fullDuplex)
451: mediumType = MEDIUM_TYPE_10_FD;
452: else
453: mediumType = MEDIUM_TYPE_10_HD;
454: }
455:
456: return mediumType;
457: }
458:
459: //---------------------------------------------------------------------------
460: // Function: _phyGetLinkStatus
461: //
462: // Purpose:
463: // Return the current link status. If the link is active, and the
464: // currently selected medium type (selectedMedium) is MEDIUM_TYPE_AUTO,
465: // then probe the PHY for the current active medium type.
466:
467: bool Intel82557::_phyGetLinkStatus(bool * linkActive,
468: mediumType_t * activeMedium,
469: mediumType_t selectedMedium)
470: {
471: mdi_reg_t reg;
472: mdi_reg_t status;
473:
474: *linkActive = false;
475: *activeMedium = MEDIUM_TYPE_INVALID;
476:
477: // If PHY was not setup properly, return immediately.
478: //
479: if (selectedMedium >= MEDIUM_TYPE_INVALID)
480: return false;
481:
482: // Determine link status. Read PHY status register twice to clear
483: // any latched link failure conditions.
484: //
485: _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
486: if (!(status & MDI_STATUS_LINK_STATUS))
487: _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
488:
489: if (!(status & MDI_STATUS_LINK_STATUS))
490: *linkActive = false;
491: else
492: *linkActive = true;
493:
494: // If the current selected medium is not AUTO, and the link is active.
495: // Return the selected medium as the active medium.
496: //
497: if (selectedMedium != MEDIUM_TYPE_AUTO) {
498: *activeMedium = selectedMedium;
499: return true; // For non-AUTO medium, we are done.
500: }
501:
502: // For AUTO medium, if auto-negotiation is not complete, or a valid
503: // link has not been detected, then return link status without
504: // setting activeMedium.
505: //
506: if (!(status & MDI_STATUS_AUTONEG_COMPLETE) ||
507: !(status & MDI_STATUS_LINK_STATUS)) {
508: return true; // should we restart auto-negotiation?
509: }
510:
511: // i82553 has a special register for determining the speed and
512: // duplex mode settings.
513: //
514: if ((phyID == PHY_MODEL_I82553_A_B) || (phyID == PHY_MODEL_I82553_C)) {
515:
516: _mdiReadPHY(phyAddr, I82553_REG_SCR, ®);
517:
518: *activeMedium = _phyGetMediumTypeFromBits(reg & I82553_SCR_100,
519: reg & I82553_SCR_FULL_DUPLEX,
520: reg & I82553_SCR_T4);
521: return true;
522: }
523:
524: // For NSC83840, we use the 83840 specific register to determine the
525: // link speed and duplex mode setting. Early 83840 devices did not
526: // properly report the remote capabilities when the remote link
527: // partner does not support NWay.
528: //
529: if (phyID == PHY_MODEL_NSC83840) {
530: mdi_reg_t exp;
531:
532: _mdiReadPHY(phyAddr, MDI_REG_ANEX, &exp);
533:
534: if ((exp & MDI_ANEX_LP_AUTONEGOTIABLE) == 0) {
535: _mdiReadPHY(phyAddr, NSC83840_REG_PAR, ®);
536:
537: *activeMedium = _phyGetMediumTypeFromBits(
538: !(reg & NSC83840_PAR_SPEED_10),
539: (reg & NSC83840_PAR_DUPLEX_STAT),
540: 0);
541: return true;
542: }
543: }
544:
545: // For generic PHY, use the standard PHY registers.
546: //
547: // Use the local and remote capability words to determine the
548: // current active medium.
549: //
550: mdi_reg_t lpa;
551: mdi_reg_t mya;
552:
553: _mdiReadPHY(phyAddr, MDI_REG_ANLP, &lpa);
554: _mdiReadPHY(phyAddr, MDI_REG_ANAR, &mya);
555:
556: mya &= lpa; // obtain common capabilities mask.
557:
558: // Observe PHY medium precedence.
559: //
560: if (mya & MDI_ANAR_TX_FD)
561: *activeMedium = MEDIUM_TYPE_TX_FD;
562: else if (mya & MDI_ANAR_T4)
563: *activeMedium = MEDIUM_TYPE_T4;
564: else if (mya & MDI_ANAR_TX_HD)
565: *activeMedium = MEDIUM_TYPE_TX_HD;
566: else if (mya & MDI_ANAR_10_FD)
567: *activeMedium = MEDIUM_TYPE_10_FD;
568: else
569: *activeMedium = MEDIUM_TYPE_10_HD;
570:
571: return true;
572: }
573:
574: //---------------------------------------------------------------------------
575: // Function: _phyGetMediumWithCode
576: //
577: // Purpose:
578: // Returns the medium object associated with the given code.
579:
580: IONetworkMedium * Intel82557::_phyGetMediumWithCode(UInt32 code)
581: {
582: if (code < MEDIUM_TYPE_INVALID)
583: return mediumTable[code];
584: else
585: return 0;
586: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.