#include "i82557.h"
#include "i82557PHY.h"
static inline void
_logMDIStatus(mdi_reg_t reg)
{
if (reg & MDI_STATUS_T4)
IOLog("PHY: T4 capable\n");
if (reg & MDI_STATUS_TX_FD)
IOLog("PHY: 100Base-TX full duplex capable\n");
if (reg & MDI_STATUS_TX_HD)
IOLog("PHY: 100Base-TX half duplex capable\n");
if (reg & MDI_STATUS_10_FD)
IOLog("PHY: 10Base-T full duplex capable\n");
if (reg & MDI_STATUS_10_HD)
IOLog("PHY: 10Base-T half duplex capable\n");
if (reg & MDI_STATUS_EXTENDED_CAPABILITY)
IOLog("PHY: has extended capability registers\n");
if (reg & MDI_STATUS_JABBER_DETECTED)
IOLog("PHY: jabberDetect set\n");
if (reg & MDI_STATUS_AUTONEG_CAPABLE)
IOLog("PHY: auto negotiation capable\n");
IOLog("PHY: link is %s\n", (reg & MDI_STATUS_LINK_STATUS) ? "UP" : "DOWN");
return;
}
UInt32 Intel82557::_phyGetID()
{
UInt16 id1, id2;
_mdiReadPHY(phyAddr, MDI_REG_PHYID_WORD_1, &id1);
_mdiReadPHY(phyAddr, MDI_REG_PHYID_WORD_2, &id2);
return ((id2 << 16) | id1);
}
bool Intel82557::_phySetMedium(mediumType_t medium)
{
mdi_reg_t status;
mdi_reg_t control;
mediumType_t phyMedium = medium;
UInt32 mediumCapableMask;
_phyReset();
_mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
mediumCapableMask = (status >> 11) & 0x1f;
if (phyMedium != MEDIUM_TYPE_AUTO) {
if ((MEDIUM_TYPE_TO_MASK(phyMedium) & mediumCapableMask) == 0) {
return false;
}
else {
bool speed100 = false;
bool fullDuplex = false;
if ((medium == MEDIUM_TYPE_TX_HD) ||
(medium == MEDIUM_TYPE_TX_FD) ||
(medium == MEDIUM_TYPE_T4))
speed100 = true;
if ((medium == MEDIUM_TYPE_10_FD) || (medium == MEDIUM_TYPE_TX_FD))
fullDuplex = true;
IOSleep(300);
control = ((speed100 ? MDI_CONTROL_100 : 0) |
(fullDuplex ? MDI_CONTROL_FULL_DUPLEX : 0));
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
VPRINT("%s: user forced %s Mbit/s%s mode\n", getName(),
speed100 ? "100" : "10",
fullDuplex ? " full duplex" : "");
IOSleep(50);
}
}
else {
control = MDI_CONTROL_AUTONEG_ENABLE;
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
IOSleep(1);
control |= MDI_CONTROL_RESTART_AUTONEG;
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
}
if (phyID == PHY_MODEL_NSC83840) {
mdi_reg_t reg;
VPRINT("%s: setting NSC83840-specific registers\n", getName());
_mdiReadPHY(phyAddr, NSC83840_REG_PCR, ®);
reg |= (NSC83840_PCR_TXREADY | NSC83840_PCR_CIM_DIS);
_mdiWritePHY(phyAddr, NSC83840_REG_PCR, reg);
}
currentMediumType = medium;
return true;
}
bool Intel82557::_phyAddMediumType(UInt32 type, UInt32 speed, UInt32 code)
{
IONetworkMedium * medium;
bool ret = false;
medium = IONetworkMedium::medium(type, speed, 0, code);
if (medium) {
ret = IONetworkMedium::addMedium(mediumDict, medium);
if (ret)
mediumTable[code] = medium;
medium->release();
}
return ret;
}
#define MBPS 1000000
void Intel82557::_phyPublishMedia()
{
mdi_reg_t status;
_mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
_phyAddMediumType(kIOMediumEthernetAuto,
0,
MEDIUM_TYPE_AUTO);
if (status & MDI_STATUS_10_HD)
_phyAddMediumType(kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex,
10 * MBPS,
MEDIUM_TYPE_10_HD);
if (status & MDI_STATUS_10_FD)
_phyAddMediumType(kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex,
10 * MBPS,
MEDIUM_TYPE_10_FD);
if (status & MDI_STATUS_TX_HD)
_phyAddMediumType(
kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex,
100 * MBPS,
MEDIUM_TYPE_TX_HD);
if (status & MDI_STATUS_TX_FD)
_phyAddMediumType(
kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex,
100 * MBPS,
MEDIUM_TYPE_TX_FD);
if (status & MDI_STATUS_T4)
_phyAddMediumType(kIOMediumEthernet100BaseT4,
100 * MBPS,
MEDIUM_TYPE_T4);
}
#define PHY_RESET_TIMEOUT 100 // ms
#define PHY_RESET_DELAY 10 // ms
#define PHY_POST_RESET_DELAY 300 // us
bool Intel82557::_phyReset()
{
int i = PHY_RESET_TIMEOUT;
mdi_reg_t control;
if (!_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control))
return false;
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control | MDI_CONTROL_RESET);
while (i > 0) {
if (!_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control))
return false;
if ((control & MDI_CONTROL_RESET) == 0) {
IODelay(PHY_POST_RESET_DELAY);
return true;
}
IOSleep(PHY_RESET_DELAY);
i -= PHY_RESET_DELAY;
}
return false;
}
#define PHY_NWAY_TIMEOUT 5000 // ms
#define PHY_NWAY_DELAY 20 // ms
bool Intel82557::_phyWaitAutoNegotiation()
{
int i = PHY_NWAY_TIMEOUT;
mdi_reg_t status;
while (i > 0) {
if (!_mdiReadPHY(phyAddr, MDI_REG_STATUS, &status))
return false;
if (status & MDI_STATUS_AUTONEG_COMPLETE)
return true;
IOSleep(PHY_NWAY_DELAY);
i -= PHY_NWAY_DELAY;
}
return false;
}
#define AUTONEGOTIATE_TIMEOUT 35
bool Intel82557::_phyProbe()
{
bool foundPhy1 = false;
mdi_reg_t control;
mdi_reg_t status;
if (phyAddr == PHY_ADDRESS_I82503) {
VPRINT("%s: overriding to use Intel 82503", getName());
return true;
}
if (phyAddr > 0 && phyAddr < PHY_ADDRESS_MAX) {
VPRINT("%s: looking for Phy 1 at address %d\n", getName(), phyAddr);
_mdiReadPHY(phyAddr, MDI_REG_CONTROL, &control);
_mdiReadPHY(phyAddr, MDI_REG_STATUS, &status); _mdiReadPHY(phyAddr, MDI_REG_STATUS, &status);
if (control == 0xffff || (status == 0 && control == 0))
{
VPRINT("%s: Phy 1 at address %d does not exist\n", getName(),
phyAddr);
}
else {
VPRINT("%s: Phy 1 at address %d exists\n", getName(), phyAddr);
foundPhy1 = true;
if (status & MDI_STATUS_LINK_STATUS) {
VPRINT("%s: found Phy 1 at address %d with link\n",
getName(), phyAddr);
return true; }
}
}
_mdiReadPHY(PHY_ADDRESS_0, MDI_REG_CONTROL, &control);
_mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
if (control == 0xffff || (status == 0 && control == 0)) {
if (phyAddr == 0) {
IOLog("%s: phy0 not detected\n", getName());
return false;
}
if (foundPhy1 == true) {
VPRINT("%s: no Phy at address 0, using Phy 1 without link\n",
getName());
return true; }
VPRINT("%s: no Phy at address 0, defaulting to 82503\n", getName());
phyAddr = PHY_ADDRESS_I82503;
return true;
}
if (foundPhy1 == true) {
control = MDI_CONTROL_ISOLATE;
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
IOSleep(1);
}
VPRINT("%s: starting auto-negotiation on Phy 0", getName());
control = MDI_CONTROL_AUTONEG_ENABLE;
_mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
IOSleep(1);
control |= MDI_CONTROL_RESTART_AUTONEG;
_mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
for (int i = 0; i < AUTONEGOTIATE_TIMEOUT; i++) {
_mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
if (status & MDI_STATUS_AUTONEG_COMPLETE)
break;
IOSleep(100);
}
_mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
_mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
_mdiReadPHY(PHY_ADDRESS_0, MDI_REG_STATUS, &status);
if ((status & MDI_STATUS_LINK_STATUS) || foundPhy1 == false) {
VPRINT("%s: using Phy 0 at address 0\n", getName());
phyAddr = 0;
return true;
}
VPRINT("%s: using Phy 1 without link\n", getName());
control = MDI_CONTROL_ISOLATE;
_mdiWritePHY(PHY_ADDRESS_0, MDI_REG_CONTROL, control);
IOSleep(1);
control = MDI_CONTROL_AUTONEG_ENABLE;
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
IOSleep(1);
control |= MDI_CONTROL_RESTART_AUTONEG;
_mdiWritePHY(phyAddr, MDI_REG_CONTROL, control);
phyID = _phyGetID();
VPRINT("%s: PHY model id is 0x%08lx\n", getName(), phyID);
phyID &= PHY_MODEL_MASK;
return true;
}
mediumType_t Intel82557::_phyGetMediumTypeFromBits(bool rate100,
bool fullDuplex,
bool t4)
{
mediumType_t mediumType;
if (t4) {
mediumType = MEDIUM_TYPE_T4;
}
else if (rate100) {
if (fullDuplex)
mediumType = MEDIUM_TYPE_TX_FD;
else
mediumType = MEDIUM_TYPE_TX_HD;
}
else {
if (fullDuplex)
mediumType = MEDIUM_TYPE_10_FD;
else
mediumType = MEDIUM_TYPE_10_HD;
}
return mediumType;
}
IONetworkMedium * Intel82557::_phyGetMediumWithType(UInt32 type)
{
if (type < MEDIUM_TYPE_INVALID)
return mediumTable[type];
else
return 0;
}
void Intel82557::_phyReportLinkStatus( bool firstPoll )
{
UInt16 phyStatus;
UInt16 phyStatusChange;
_mdiReadPHY( phyAddr, MDI_REG_STATUS, &phyStatus );
phyStatusChange = ( phyStatusPrev ^ phyStatus ) &
( MDI_STATUS_LINK_STATUS |
MDI_STATUS_AUTONEG_COMPLETE );
if ( phyStatusChange || firstPoll )
{
if ( firstPoll )
{
_phyWaitAutoNegotiation();
_mdiReadPHY( phyAddr, MDI_REG_STATUS, &phyStatus );
_mdiReadPHY( phyAddr, MDI_REG_STATUS, &phyStatus );
}
if ( phyStatus & MDI_STATUS_LINK_STATUS )
{
IONetworkMedium * activeMedium;
activeMedium = _phyGetMediumWithType( _phyGetActiveMedium() );
setLinkStatus( kIONetworkLinkValid | kIONetworkLinkActive,
activeMedium );
}
else
{
setLinkStatus( kIONetworkLinkValid, 0 );
}
phyStatusPrev = phyStatus;
}
}
mediumType_t Intel82557::_phyGetActiveMedium()
{
mdi_reg_t reg;
mediumType_t medium;
do {
if ( currentMediumType != MEDIUM_TYPE_AUTO )
{
medium = currentMediumType;
break;
}
if ( ( phyID == PHY_MODEL_I82553_A_B ) ||
( phyID == PHY_MODEL_I82553_C ) )
{
_mdiReadPHY( phyAddr, I82553_REG_SCR, ® );
medium = _phyGetMediumTypeFromBits( reg & I82553_SCR_100,
reg & I82553_SCR_FULL_DUPLEX,
reg & I82553_SCR_T4 );
break;
}
else if ( phyID == PHY_MODEL_NSC83840 )
{
mdi_reg_t exp;
_mdiReadPHY( phyAddr, MDI_REG_ANEX, &exp );
if ( ( exp & MDI_ANEX_LP_AUTONEGOTIABLE ) == 0 )
{
_mdiReadPHY( phyAddr, NSC83840_REG_PAR, ® );
medium = _phyGetMediumTypeFromBits(
!(reg & NSC83840_PAR_SPEED_10),
(reg & NSC83840_PAR_DUPLEX_STAT),
0 );
break;
}
}
mdi_reg_t lpa;
mdi_reg_t mya;
_mdiReadPHY( phyAddr, MDI_REG_ANLP, &lpa );
_mdiReadPHY( phyAddr, MDI_REG_ANAR, &mya );
mya &= lpa;
if ( mya & MDI_ANAR_TX_FD ) medium = MEDIUM_TYPE_TX_FD;
else if ( mya & MDI_ANAR_T4 ) medium = MEDIUM_TYPE_T4;
else if ( mya & MDI_ANAR_TX_HD ) medium = MEDIUM_TYPE_TX_HD;
else if ( mya & MDI_ANAR_10_FD ) medium = MEDIUM_TYPE_10_FD;
else medium = MEDIUM_TYPE_10_HD;
}
while ( false );
return medium;
}