]> git.saurik.com Git - apple/xnu.git/blob - iokit/Drivers/network/drvIntel82557/i82557PHY.cpp
3c36d550a1f821c403e12f29dc1675d45108f068
[apple/xnu.git] / iokit / Drivers / network / drvIntel82557 / i82557PHY.cpp
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, &reg);
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(kIOMediumEthernetAuto,
220 0,
221 MEDIUM_TYPE_AUTO);
222
223 if (status & MDI_STATUS_10_HD)
224 _phyAddMediumType(kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex,
225 10 * MBPS,
226 MEDIUM_TYPE_10_HD);
227
228 if (status & MDI_STATUS_10_FD)
229 _phyAddMediumType(kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex,
230 10 * MBPS,
231 MEDIUM_TYPE_10_FD);
232
233 if (status & MDI_STATUS_TX_HD)
234 _phyAddMediumType(
235 kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex,
236 100 * MBPS,
237 MEDIUM_TYPE_TX_HD);
238
239 if (status & MDI_STATUS_TX_FD)
240 _phyAddMediumType(
241 kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex,
242 100 * MBPS,
243 MEDIUM_TYPE_TX_FD);
244
245 if (status & MDI_STATUS_T4)
246 _phyAddMediumType(kIOMediumEthernet100BaseT4,
247 100 * MBPS,
248 MEDIUM_TYPE_T4);
249 }
250
251 //---------------------------------------------------------------------------
252 // Function: _phyReset
253 //
254 // Purpose:
255 // Reset the PHY.
256
257 #define PHY_RESET_TIMEOUT 100 // ms
258 #define PHY_RESET_DELAY 10 // ms
259 #define PHY_POST_RESET_DELAY 300 // us
260
261 bool Intel82557::_phyReset()
262 {
263 int i = PHY_RESET_TIMEOUT;
264 mdi_reg_t control;
265
266 if (!_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control))
267 return false;
268
269 // Set the reset bit in the PHY Control register
270 //
271 _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control | MDI_CONTROL_RESET);
272
273 // Wait till reset process is complete (MDI_CONTROL_RESET returns to zero)
274 //
275 while (i > 0) {
276 if (!_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control))
277 return false;
278 if ((control & MDI_CONTROL_RESET) == 0) {
279 IODelay(PHY_POST_RESET_DELAY);
280 return true;
281 }
282 IOSleep(PHY_RESET_DELAY);
283 i -= PHY_RESET_DELAY;
284 }
285 return false;
286 }
287
288 //---------------------------------------------------------------------------
289 // Function: _phyWaitAutoNegotiation
290 //
291 // Purpose:
292 // Wait until auto-negotiation is complete.
293
294 #define PHY_NWAY_TIMEOUT 5000 // ms
295 #define PHY_NWAY_DELAY 20 // ms
296
297 bool Intel82557::_phyWaitAutoNegotiation()
298 {
299 int i = PHY_NWAY_TIMEOUT;
300 mdi_reg_t status;
301
302 while (i > 0) {
303 if (!_mdiReadPHY(phyAddr, MDI_REG_STATUS, &status))
304 return false;
305
306 if (status & MDI_STATUS_AUTONEG_COMPLETE)
307 return true;
308
309 IOSleep(PHY_NWAY_DELAY);
310 i -= PHY_NWAY_DELAY;
311 }
312 return false;
313 }
314
315 //---------------------------------------------------------------------------
316 // Function: _phyProbe
317 //
318 // Purpose:
319 // Find out which PHY is active.
320 //
321 #define AUTONEGOTIATE_TIMEOUT 35
322
323 bool Intel82557::_phyProbe()
324 {
325 bool foundPhy1 = false;
326 mdi_reg_t control;
327 mdi_reg_t status;
328
329 if (phyAddr == PHY_ADDRESS_I82503) {
330 VPRINT("%s: overriding to use Intel 82503", getName());
331 return true;
332 }
333
334 if (phyAddr > 0 && phyAddr < PHY_ADDRESS_MAX) {
335 VPRINT("%s: looking for Phy 1 at address %d\n", getName(), phyAddr);
336 _mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control);
337 _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status); // do it twice
338 _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
339 if (control == 0xffff || (status == 0 && control == 0))
340 {
341 VPRINT("%s: Phy 1 at address %d does not exist\n", getName(),
342 phyAddr);
343 }
344 else {
345 VPRINT("%s: Phy 1 at address %d exists\n", getName(), phyAddr);
346 foundPhy1 = true;
347 if (status & MDI_STATUS_LINK_STATUS) {
348 VPRINT("%s: found Phy 1 at address %d with link\n",
349 getName(), phyAddr);
350 return true; // use PHY1
351 }
352 }
353 }
354
355 // PHY1 does not exist, or it does not have valid link.
356 // Try PHY0 at address 0.
357 //
358 _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_CONTROL, &control);
359 _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
360
361 if (control == 0xffff || (status == 0 && control == 0)) {
362 if (phyAddr == 0) { /* if address forced to 0, then fail */
363 IOLog("%s: phy0 not detected\n", getName());
364 return false;
365 }
366 if (foundPhy1 == true) {
367 VPRINT("%s: no Phy at address 0, using Phy 1 without link\n",
368 getName());
369 return true; // use PHY1 without a valid link
370 }
371 VPRINT("%s: no Phy at address 0, defaulting to 82503\n", getName());
372 phyAddr = PHY_ADDRESS_I82503;
373 return true;
374 }
375
376 // must isolate PHY1 electrically before using PHY0.
377 //
378 if (foundPhy1 == true) {
379 control = MDI_CONTROL_ISOLATE;
380 _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
381 IOSleep(1);
382 }
383
384 // Enable and restart auto-negotiation on PHY0.
385 //
386 VPRINT("%s: starting auto-negotiation on Phy 0", getName());
387 control = MDI_CONTROL_AUTONEG_ENABLE;
388 _mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
389 IOSleep(1);
390 control |= MDI_CONTROL_RESTART_AUTONEG;
391 _mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
392
393 for (int i = 0; i < AUTONEGOTIATE_TIMEOUT; i++) {
394 _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
395 if (status & MDI_STATUS_AUTONEG_COMPLETE)
396 break;
397 IOSleep(100);
398 }
399 _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
400 _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
401 _mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
402 if ((status & MDI_STATUS_LINK_STATUS) || foundPhy1 == false) {
403 VPRINT("%s: using Phy 0 at address 0\n", getName());
404 phyAddr = 0;
405 return true;
406 }
407
408 // Isolate PHY0.
409 //
410 VPRINT("%s: using Phy 1 without link\n", getName());
411 control = MDI_CONTROL_ISOLATE;
412 _mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
413 IOSleep(1);
414
415 // Enable and restart auto-negotiation on PHY1.
416 //
417 control = MDI_CONTROL_AUTONEG_ENABLE;
418 _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
419 IOSleep(1);
420 control |= MDI_CONTROL_RESTART_AUTONEG;
421 _mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
422
423 phyID = _phyGetID();
424 VPRINT("%s: PHY model id is 0x%08lx\n", getName(), phyID);
425 phyID &= PHY_MODEL_MASK;
426
427 return true;
428 }
429
430 //---------------------------------------------------------------------------
431 // Function: _phyGetMediumTypeFromBits
432 //
433 // Purpose:
434 // Return the medium type that correspond to the given specifiers.
435
436 mediumType_t Intel82557::_phyGetMediumTypeFromBits(bool rate100,
437 bool fullDuplex,
438 bool t4)
439 {
440 mediumType_t mediumType;
441
442 if (t4) {
443 mediumType = MEDIUM_TYPE_T4;
444 }
445 else if (rate100) {
446 if (fullDuplex)
447 mediumType = MEDIUM_TYPE_TX_FD;
448 else
449 mediumType = MEDIUM_TYPE_TX_HD;
450 }
451 else {
452 if (fullDuplex)
453 mediumType = MEDIUM_TYPE_10_FD;
454 else
455 mediumType = MEDIUM_TYPE_10_HD;
456 }
457
458 return mediumType;
459 }
460
461 //---------------------------------------------------------------------------
462 // Function: _phyGetMediumWithCode
463 //
464 // Purpose:
465 // Returns the IONetworkMedium object associated with the given type.
466
467 IONetworkMedium * Intel82557::_phyGetMediumWithType(UInt32 type)
468 {
469 if (type < MEDIUM_TYPE_INVALID)
470 return mediumTable[type];
471 else
472 return 0;
473 }
474
475 //---------------------------------------------------------------------------
476 // Function: _phyReportLinkStatus
477 //
478 // Purpose:
479 // Called periodically to monitor for link changes. When a change
480 // is detected, determine the current link and report it to the
481 // upper layers by calling IONetworkController::setLinkStatus().
482
483 void Intel82557::_phyReportLinkStatus( bool firstPoll = false )
484 {
485 UInt16 phyStatus;
486 UInt16 phyStatusChange;
487
488 // Read PHY status register.
489
490 _mdiReadPHY( phyAddr, MDI_REG_STATUS, &phyStatus );
491
492 // Detect a change in the two link related bits.
493 // Remember that the link status bit will latch a link fail
494 // condition (should not miss a link down event).
495
496 phyStatusChange = ( phyStatusPrev ^ phyStatus ) &
497 ( MDI_STATUS_LINK_STATUS |
498 MDI_STATUS_AUTONEG_COMPLETE );
499
500 if ( phyStatusChange || firstPoll )
501 {
502 if ( firstPoll )
503 {
504 // For the initial link status poll, wait a bit, then
505 // re-read the status register to clear any latched bits.
506
507 _phyWaitAutoNegotiation();
508 _mdiReadPHY( phyAddr, MDI_REG_STATUS, &phyStatus );
509 _mdiReadPHY( phyAddr, MDI_REG_STATUS, &phyStatus );
510 }
511
512 // IOLog("PhyStatus: %04x\n", phyStatus);
513
514 // Determine the link status.
515
516 if ( ( phyStatus & MDI_STATUS_LINK_STATUS ) &&
517 ( phyStatus & MDI_STATUS_AUTONEG_COMPLETE ) )
518 {
519 // Excellent, link is up.
520
521 IONetworkMedium * activeMedium;
522
523 activeMedium = _phyGetMediumWithType( _phyGetActiveMedium() );
524
525 setLinkStatus( kIONetworkLinkValid | kIONetworkLinkActive,
526 activeMedium );
527
528 // IOLog("link is up %lx\n",
529 // activeMedium ? activeMedium->getType() : 0);
530 }
531 else
532 {
533 // Link is down.
534
535 setLinkStatus( kIONetworkLinkValid, 0 );
536
537 // IOLog("link is down\n");
538 }
539
540 // Save phyStatus for the next run.
541
542 phyStatusPrev = phyStatus;
543 }
544 }
545
546 //---------------------------------------------------------------------------
547 // Function: _phyGetActiveMedium
548 //
549 // Purpose:
550 // Once the PHY reports that the link is up, this method can be called
551 // to return the type of link that was established.
552
553 mediumType_t Intel82557::_phyGetActiveMedium()
554 {
555 mdi_reg_t reg;
556 mediumType_t medium;
557
558 do {
559 // For the simple case where the media selection is not
560 // automatic (e.g. forced to 100BaseTX).
561
562 if ( currentMediumType != MEDIUM_TYPE_AUTO )
563 {
564 medium = currentMediumType;
565 break;
566 }
567
568 // i82553 has a special register for determining the speed and
569 // duplex mode settings.
570
571 if ( ( phyID == PHY_MODEL_I82553_A_B ) ||
572 ( phyID == PHY_MODEL_I82553_C ) )
573 {
574 _mdiReadPHY( phyAddr, I82553_REG_SCR, &reg );
575
576 medium = _phyGetMediumTypeFromBits( reg & I82553_SCR_100,
577 reg & I82553_SCR_FULL_DUPLEX,
578 reg & I82553_SCR_T4 );
579 break;
580 }
581 else if ( phyID == PHY_MODEL_NSC83840 )
582 {
583 // For NSC83840, we use the 83840 specific register to determine
584 // the link speed and duplex mode setting. Early 83840 devices
585 // did not seem to report the remote capabilities when the link
586 // partner does not support NWay.
587
588 mdi_reg_t exp;
589
590 _mdiReadPHY( phyAddr, MDI_REG_ANEX, &exp );
591
592 if ( ( exp & MDI_ANEX_LP_AUTONEGOTIABLE ) == 0 )
593 {
594 _mdiReadPHY( phyAddr, NSC83840_REG_PAR, &reg );
595
596 medium = _phyGetMediumTypeFromBits(
597 !(reg & NSC83840_PAR_SPEED_10),
598 (reg & NSC83840_PAR_DUPLEX_STAT),
599 0 );
600 break;
601 }
602 }
603
604 // For generic PHY, use the standard PHY registers.
605 //
606 // Use the local and remote capability words to determine the
607 // current active medium.
608
609 mdi_reg_t lpa;
610 mdi_reg_t mya;
611
612 _mdiReadPHY( phyAddr, MDI_REG_ANLP, &lpa );
613 _mdiReadPHY( phyAddr, MDI_REG_ANAR, &mya );
614
615 mya &= lpa; // obtain common capabilities mask.
616
617 // Observe PHY medium precedence.
618
619 if ( mya & MDI_ANAR_TX_FD ) medium = MEDIUM_TYPE_TX_FD;
620 else if ( mya & MDI_ANAR_T4 ) medium = MEDIUM_TYPE_T4;
621 else if ( mya & MDI_ANAR_TX_HD ) medium = MEDIUM_TYPE_TX_HD;
622 else if ( mya & MDI_ANAR_10_FD ) medium = MEDIUM_TYPE_10_FD;
623 else medium = MEDIUM_TYPE_10_HD;
624 }
625 while ( false );
626
627 return medium;
628 }