#include "BCM440XPHY.h"
#define kPHYName "bcm_phy"
#define super OSObject
OSDefineMetaClassAndStructors( AppleBCM440XPHY, OSObject )
bool AppleBCM440XPHY::init( void * owner, MDIReadFunc readFunc,
MDIWriteFunc writeFunc )
{
if (super::init() == false)
return false;
if (readFunc == 0 || writeFunc == 0)
return false;
fPHYOwner = owner;
fMDIReadFunc = readFunc;
fMDIWriteFunc = writeFunc;
setConfiguredLink(kMIILinkNone);
return true;
}
AppleBCM440XPHY * AppleBCM440XPHY::BCM440XPHY( void * owner,
MDIReadFunc readFunc,
MDIWriteFunc writeFunc )
{
AppleBCM440XPHY * phy = new AppleBCM440XPHY;
if (phy && !phy->init(owner, readFunc, writeFunc))
{
phy->release();
phy = 0;
}
return phy;
}
bool AppleBCM440XPHY::mdiReadWord( PHYAddr phyAddr, PHYReg phyReg,
PHYWord * phyData )
{
return fMDIReadFunc(fPHYOwner, phyAddr, phyReg, phyData);
}
void AppleBCM440XPHY::mdiWriteWord( PHYAddr phyAddr, PHYReg phyReg,
PHYWord phyData )
{
fMDIWriteFunc(fPHYOwner, phyAddr, phyReg, phyData);
}
IOReturn AppleBCM440XPHY::probePHY( void )
{
PHYWord id0, id1;
PHY_LOG("%s::%s\n", kPHYName, __FUNCTION__);
fPHYAddr = kPHYAddrInvalid;
for (int addr = kPHYAddrMin; addr <= kPHYAddrMax; addr++)
{
if (mdiReadWord(addr, kMIIRegisterStatus, &fPHYStatus) &&
fPHYStatus != 0 &&
mdiReadWord(addr, kMIIRegisterID0, &id0) &&
mdiReadWord(addr, kMIIRegisterID1, &id1))
{
fPHYID = ((UInt32)(id0) << 16) | id1;
fPHYAddr = addr;
PHY_LOG("Found PHY @ 0x%lx status %04x ID 0x%08lx\n",
fPHYAddr, fPHYStatus, fPHYID);
return kIOReturnSuccess;
}
}
return kIOReturnNoDevice;
}
IOReturn AppleBCM440XPHY::resetPHY( void )
{
PHYWord control;
IOReturn ior;
PHY_LOG("%s::%s\n", kPHYName, __FUNCTION__);
if (mdiReadWord(fPHYAddr, kMIIRegisterControl, &control) == false)
return kIOReturnIOError;
mdiWriteWord(fPHYAddr, kMIIRegisterControl, control | kMIIControlReset);
ior = kIOReturnTimeout;
for ( SInt32 timeout = kPHYResetTimeout;
timeout > 0; timeout -= kPHYResetPause )
{
if (mdiReadWord(fPHYAddr, kMIIRegisterControl, &control) == false)
{
ior = kIOReturnIOError;
break;
}
if ((control & kMIIControlReset) == 0)
{
ior = kIOReturnSuccess;
break;
}
IOSleep(kPHYResetPause);
}
if (ior != kIOReturnSuccess)
{
PHY_LOG("%s::%s error 0x%x\n", kPHYName, __FUNCTION__, ior);
}
return ior;
}
UInt32 AppleBCM440XPHY::getPHYAddress( void ) const
{
return fPHYAddr;
}
UInt32 AppleBCM440XPHY::getPHYIdentifier( void ) const
{
return fPHYID;
}
UInt32 AppleBCM440XPHY::getLocalLinkSupportMask( void ) const
{
return ((fPHYStatus & kMIIStatusLinkMask) >> kMIIStatusLinkShift);
}
UInt32 AppleBCM440XPHY::getActiveLink( void ) const
{
return fPHYActiveLink;
}
void AppleBCM440XPHY::setConfiguredLink( MIILink configuredLink )
{
fPHYLastStatus = 0;
fPHYActiveLink = kMIILinkNone;
fPHYConfiguredLink = configuredLink;
}
IOReturn AppleBCM440XPHY::waitForStatusBits( UInt32 timeoutMS,
PHYWord statusMask,
PHYWord statusValue )
{
PHYWord status;
IOReturn ior = kIOReturnTimeout;
PHY_LOG("%s::%s(%ld)\n", kPHYName, __FUNCTION__, timeoutMS);
if (timeoutMS == 0) return kIOReturnSuccess;
for ( SInt32 t = timeoutMS; t > 0; t -= 10 )
{
if (mdiReadWord(fPHYAddr, kMIIRegisterStatus, &status) == false)
{
ior = kIOReturnIOError;
break;
}
if ((status & statusMask) == statusValue)
{
ior = kIOReturnSuccess;
break;
}
IOSleep(10);
}
if (ior != kIOReturnSuccess)
{
PHY_LOG("%s::%s error 0x%x\n", kPHYName, __FUNCTION__, ior);
}
return ior;
}
IOReturn AppleBCM440XPHY::restartAutoNegotiation( UInt32 waitTimeoutMS )
{
PHYWord word;
UInt32 linkMask = kMIILinkMask;
IOReturn ior;
PHY_LOG("%s::%s(%ld)\n", kPHYName, __FUNCTION__, waitTimeoutMS);
#if PRE_NWAY_RESET
if ((ior = resetPHY()) != kIOReturnSuccess)
{
PHY_LOG("%s::%s PHY reset 1 error\n", kPHYName, __FUNCTION__);
return ior;
}
#endif
if ((fPHYStatus & kMIIStatusAutoNegotiationAbility) == 0)
{
PHY_LOG("%s::%s no Nway support\n", kPHYName, __FUNCTION__);
return kIOReturnUnsupported;
}
linkMask &= getLocalLinkSupportMask();
if (linkMask == 0)
{
PHY_LOG("%s::%s Link mask is zero\n", kPHYName, __FUNCTION__);
return kIOReturnUnsupported;
}
setConfiguredLink(kMIILinkAutoNeg);
if (mdiReadWord(fPHYAddr, kMIIRegisterAdvertisement, &word) == false)
{
PHY_LOG("%s::%s: ANAR read error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
word &= ~(kMIIAdvertisementLinkMask | kMIIAdvertisementPauseCapable);
word |= (linkMask << kMIIAdvertisementLinkShift) & kMIIAdvertisementLinkMask;
mdiWriteWord(fPHYAddr, kMIIRegisterAdvertisement, word);
if (mdiReadWord(fPHYAddr, kMIIRegisterControl, &word) == false)
{
PHY_LOG("%s::%s CONTROL read error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
word &= ~(kMIIControlPowerDown | kMIIControlIsolate | kMIIControlLoopback);
word |= (kMIIControlEnableAutoNegotiation |
kMIIControlRestartAutoNegotiation);
mdiWriteWord(fPHYAddr, kMIIRegisterControl, word);
#if POST_NWAY_RESET
if ((ior = resetPHY()) != kIOReturnSuccess)
{
PHY_LOG("%s::%s PHY reset 2 error\n", kPHYName, __FUNCTION__);
return ior;
}
#endif
return waitForStatusBits( waitTimeoutMS,
kMIIStatusAutoNegotiationComplete,
kMIIStatusAutoNegotiationComplete );
}
IOReturn AppleBCM440XPHY::forceSpeedAndDuplex( MIILink forceLink,
UInt32 waitTimeoutMS )
{
PHYWord word;
IOReturn ior;
PHY_LOG("%s::%s(0x%lx, %ld)\n", kPHYName, __FUNCTION__,
forceLink, waitTimeoutMS);
if (forceLink & kMIILinkAutoNeg)
return kIOReturnBadArgument;
if ((forceLink & getLocalLinkSupportMask()) == 0)
{
PHY_LOG("%s::%s PHY does not support forced link %lx\n",
kPHYName, __FUNCTION__, forceLink);
return kIOReturnBadArgument;
}
#if PRE_FORCE_RESET
if ((ior = resetPHY()) != kIOReturnSuccess)
{
PHY_LOG("%s::%s PHY reset 1 error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
#endif
if (mdiReadWord(fPHYAddr, kMIIRegisterControl, &word) == false)
{
PHY_LOG("%s::%s CONTROL read error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
word &= ~( kMIIControlSpeedSelection |
kMIIControlFullDuplex |
kMIIControlIsolate |
kMIIControlEnableAutoNegotiation );
if (forceLink >= kMIILink100BASETX)
word |= kMIIControlSpeedSelection;
if (forceLink & (kMIILink100BASETX_FD | kMIILink10BASET_FD))
word |= kMIIControlFullDuplex;
mdiWriteWord(fPHYAddr, kMIIRegisterControl, word);
PHY_LOG("%s::%s CONTROL set to %04x\n", kPHYName, __FUNCTION__, word);
#if POST_FORCE_RESET
if ((ior = resetPHY()) != kIOReturnSuccess)
{
PHY_LOG("%s::%s PHY reset 2 error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
#endif
setConfiguredLink(forceLink);
return waitForStatusBits( waitTimeoutMS,
kMIIStatusLinkValid, kMIIStatusLinkValid );
}
IOReturn AppleBCM440XPHY::checkForLinkChange( bool * linkChanged,
IOOptionBits options )
{
PHYWord status;
if (linkChanged)
*linkChanged = false;
if (mdiReadWord(fPHYAddr, kMIIRegisterStatus, &status) == false)
{
PHY_LOG("%s::%s STATUS read 1 error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
if (options & kPHYLinkChangePoll)
{
if (((status ^ fPHYLastStatus) &
(kMIIStatusAutoNegotiationComplete | kMIIStatusLinkValid)) == 0)
{
goto done; }
}
else
{
if (mdiReadWord(fPHYAddr, kMIIRegisterStatus, &status) == false)
{
PHY_LOG("%s::%s STATUS read 2 error\n", kPHYName, __FUNCTION__);
return kIOReturnIOError;
}
}
PHY_LOG("%s::%s link changed\n", kPHYName, __FUNCTION__);
fPHYActiveLink = kMIILinkNone;
if (linkChanged)
*linkChanged = true;
if (fPHYConfiguredLink & kMIILinkAutoNeg)
{
if ((status &
(kMIIStatusLinkValid | kMIIStatusAutoNegotiationComplete)) ==
(kMIIStatusLinkValid | kMIIStatusAutoNegotiationComplete))
{
PHYWord local, remote, common;
if (mdiReadWord(fPHYAddr, kMIIRegisterAdvertisement, &local) &&
mdiReadWord(fPHYAddr, kMIIRegisterLinkPartner, &remote))
{
local = (local & kMIIAdvertisementLinkMask) >>
kMIIAdvertisementLinkShift;
remote = (remote & kMIILinkPartnerLinkMask) >>
kMIILinkPartnerLinkShift;
PHY_LOG("%s: local links: 0x%04x\n", kPHYName, local);
PHY_LOG("%s: remote links: 0x%04x\n", kPHYName, remote);
common = local & remote;
if ( common & kMIILink100BASETX_FD )
fPHYActiveLink = kMIILink100BASETX_FD;
else if ( common & kMIILink100BASET4 )
fPHYActiveLink = kMIILink100BASET4;
else if ( common & kMIILink100BASETX )
fPHYActiveLink = kMIILink100BASETX;
else if ( common & kMIILink10BASET_FD )
fPHYActiveLink = kMIILink10BASET_FD;
else if ( common & kMIILink10BASET )
fPHYActiveLink = kMIILink10BASET;
}
}
}
else
{
if (status & kMIIStatusLinkValid)
fPHYActiveLink = fPHYConfiguredLink;
}
done:
fPHYLastStatus = status;
return kIOReturnSuccess;
}
IOReturn AppleBCM440XPHY::programLink( MIILink miiLink, UInt32 waitTimeoutMS )
{
IOReturn ior;
if (miiLink & kMIILinkAutoNeg)
ior = restartAutoNegotiation(waitTimeoutMS);
else
ior = forceSpeedAndDuplex(miiLink, waitTimeoutMS);
return ior;
}