#include "RTL8139.h"
bool RTL8139::phyAddMediumType( IOMediumType type, UInt32 bps, MediumIndex index )
{
IONetworkMedium *medium;
bool ret = false;
medium = IONetworkMedium::medium( type, bps, 0, index );
if ( medium )
{
ret = IONetworkMedium::addMedium( mediumDict, medium );
if ( ret )
mediumTable[ index ] = medium;
ELG( index, medium, '+Med', "RTL8139::phyAddMediumType" );
medium->release();
}
return ret;
}
#define kMbScale 1000000
void RTL8139::phyProbeMediaCapability()
{
UInt16 anar;
reg_bms = csrRead16( RTL_BMS ); anar = csrRead16( RTL_ANAR ); ELG( anar, reg_bms, 'PPMC', "RTL8139::phyProbeMediaCapability" );
fPauseSupported = false; if ( anar & MII_ANAR_PAUSE ) fPauseSupported = true;
phyAddMediumType( kIOMediumEthernetAuto, 0, MEDIUM_INDEX_AUTO );
if ( reg_bms & MII_STATUS_TX_FD ) {
phyAddMediumType( kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex,
100 * kMbScale, MEDIUM_INDEX_TX_FD );
phyAddMediumType( kIOMediumEthernet100BaseTX
| kIOMediumOptionFullDuplex
| kIOMediumOptionFlowControl,
100 * kMbScale, MEDIUM_INDEX_TX_FC );
phyAddMediumType( kIOMediumEthernet100BaseTX | kIOMediumOptionLoopback,
100 * kMbScale, MEDIUM_INDEX_TX_LB );
}
if ( reg_bms & MII_STATUS_TX_HD ) {
phyAddMediumType( kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex,
100 * kMbScale, MEDIUM_INDEX_TX_HD );
}
if ( reg_bms & MII_STATUS_10_FD ) {
phyAddMediumType( kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex,
10 * kMbScale, MEDIUM_INDEX_10_FD );
phyAddMediumType( kIOMediumEthernet10BaseT
| kIOMediumOptionFullDuplex
| kIOMediumOptionFlowControl,
10 * kMbScale, MEDIUM_INDEX_10_FC );
phyAddMediumType( kIOMediumEthernet10BaseT | kIOMediumOptionLoopback,
10 * kMbScale, MEDIUM_INDEX_10_LB );
}
if ( reg_bms & MII_STATUS_10_HD ) {
phyAddMediumType( kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex,
10 * kMbScale, MEDIUM_INDEX_10_HD );
}
phyAddMediumType( kIOMediumEthernetNone, 0, MEDIUM_INDEX_NONE );
return;
}
bool RTL8139::phyReset()
{
ELG( 0, 0, 'rPHY', "RTL8139::phyReset" );
return true;
}
bool RTL8139::phyWaitForAutoNegotiation()
{
SInt32 timeout;
UInt16 bms;
ELG( 0, 0, 'WtAN', "RTL8139::phyWaitForAutoNegotiation" );
for ( timeout = 9000; timeout > 0; timeout -= 20 )
{
bms = csrRead16( RTL_BMS );
IOSleep( 20 ); if ( bms & MII_STATUS_AUTONEG_COMPLETE )
return true;
}
return false;
}
bool RTL8139::phySetMedium( MediumIndex mediumIndex )
{
UInt16 bmc; UInt16 anar; UInt32 tcr;
ELG( 0, mediumIndex, 'PhSM', "RTL8139::phySetMedium" );
if ( mediumIndex == currentMediumIndex )
return true;
switch ( mediumIndex )
{
case MEDIUM_INDEX_AUTO:
if ( fLoopback )
{
tcr = csrRead32( RTL_TCR ); tcr &= ~(R_TCR_LBKI | R_TCR_LBKE);
csrWrite32( RTL_TCR, tcr );
fLoopback = false;
}
anar = MII_ANAR_10_HD | MII_ANAR_10_FD
| MII_ANAR_TX_HD | MII_ANAR_TX_FD | MII_ANAR_SELECTOR_CSMACD;
csrWrite16( RTL_ANAR, anar );
csrWrite16( RTL_BMC, MII_CONTROL_AUTONEG_ENABLE | MII_CONTROL_RESTART_AUTONEG );
phyWaitForAutoNegotiation();
break;
case MEDIUM_INDEX_10_FC:
case MEDIUM_INDEX_10_FD:
case MEDIUM_INDEX_10_HD:
case MEDIUM_INDEX_TX_FC:
case MEDIUM_INDEX_TX_FD:
case MEDIUM_INDEX_TX_HD:
if ( fLoopback ) {
fLoopback = false;
tcr = csrRead32( RTL_TCR ); tcr &= ~(R_TCR_LBKI | R_TCR_LBKE);
csrWrite32( RTL_TCR, tcr );
}
anar = MII_ANAR_10_HD | MII_ANAR_10_FD | MII_ANAR_SELECTOR_CSMACD;
if ( (mediumIndex == MEDIUM_INDEX_TX_HD) || (mediumIndex == MEDIUM_INDEX_TX_FD) || (mediumIndex == MEDIUM_INDEX_TX_FC) ) { anar = MII_ANAR_TX_HD | MII_ANAR_TX_FD | MII_ANAR_SELECTOR_CSMACD;
}
if ( (mediumIndex == MEDIUM_INDEX_10_HD) || (mediumIndex == MEDIUM_INDEX_TX_HD) )
{
anar &= ~(MII_ANAR_10_FD | MII_ANAR_TX_FD); }
else
{
anar &= ~(MII_ANAR_10_HD | MII_ANAR_TX_HD); if ( mediumIndex == MEDIUM_INDEX_10_FC || mediumIndex == MEDIUM_INDEX_TX_FC ) anar |= MII_ANAR_PAUSE;
}
csrWrite16( RTL_ANAR, anar );
csrWrite16( RTL_BMC, MII_CONTROL_AUTONEG_ENABLE | MII_CONTROL_RESTART_AUTONEG );
phyWaitForAutoNegotiation();
break;
case MEDIUM_INDEX_10_LB: case MEDIUM_INDEX_TX_LB:
tcr = csrRead32( RTL_TCR ); tcr &= (R_TCR_LBKI | R_TCR_LBKE);
bmc = MII_CONTROL_RESET; if ( mediumIndex == MEDIUM_INDEX_TX_LB )
bmc |= MII_CONTROL_100;
if ( fLoopbackMode == kSelectLoopbackMAC )
{
tcr |= R_TCR_LBKI;
}
else {
tcr |= R_TCR_LBKE; bmc |= MII_CONTROL_LOOPBACK;
}
csrWrite32( RTL_TCR, tcr );
csrWrite16( RTL_BMC, bmc ); IOSleep( 1 );
fLoopback = true;
break;
case MEDIUM_INDEX_NONE:
if ( fLoopback )
{
tcr = csrRead32( RTL_TCR ); tcr &= ~(R_TCR_LBKI | R_TCR_LBKE);
csrWrite32( RTL_TCR, tcr );
fLoopback = false;
}
csrWrite16( RTL_BMC, MII_CONTROL_POWER_DOWN );
break;
default:
return false;
}
currentMediumIndex = mediumIndex;
return true;
}
bool RTL8139::phySetMedium( const IONetworkMedium* medium )
{
ELG( medium, medium->getIndex(), 'PhSM', "RTL8139::phySetMedium" );
return phySetMedium( medium->getIndex() );
}
void RTL8139::phyReportLinkStatus()
{
MediumIndex activeMediumIndex;
UInt16 anlp; UInt16 phyStatus;
UInt16 linkChanged;
UInt8 mediaStatus;
UInt16 modeControl;
phyStatus = csrRead16( RTL_BMS );
ELG( phyStatusLast, phyStatus, 'PRLS', "RTL8139::phyReportLinkStatus" );
linkChanged = (phyStatus ^ phyStatusLast)
& (MII_STATUS_LINK_STATUS | MII_STATUS_AUTONEG_COMPLETE );
if ( linkChanged || forceLinkChange )
{
if ( phyStatus & MII_STATUS_LINK_STATUS )
{
mediaStatus = csrRead8( RTL_MEDIA_STATUS );
modeControl = csrRead16( RTL_BMC );
anlp = csrRead16( RTL_ANLP );
ELG( mediaStatus << 16 | phyStatus, modeControl << 16 | anlp, '+Up+', "RTL8139::phyReportLinkStatus - 10/half" );
if ( !(anlp & MII_ANLP_PAUSE) )
ALRT( mediaStatus, anlp, 'FlC-', "RTL8139::phyReportLinkStatus - ethernet flow control not enabled.");
if ( mediaStatus & R_MEDIA_STATUS_SPEED_10 )
{
fSpeed100 = false;
if ( (modeControl & R_BMC_DUPLEXMODE) == 0 )
{
activeMediumIndex = MEDIUM_INDEX_10_HD; ELG( currentMediumIndex, activeMediumIndex, '10HD', "RTL8139::phyReportLinkStatus - 10/half" );
}
else if ( anlp & MII_ANLP_PAUSE
&& (currentMediumIndex == MEDIUM_INDEX_AUTO
|| currentMediumIndex == MEDIUM_INDEX_10_FC) )
{
activeMediumIndex = MEDIUM_INDEX_10_FC; ELG( currentMediumIndex, activeMediumIndex, '10FC', "RTL8139::phyReportLinkStatus - 10/Flow Control" );
}
else
{
activeMediumIndex = MEDIUM_INDEX_10_FD; ELG( currentMediumIndex, activeMediumIndex, '10FD', "RTL8139::phyReportLinkStatus - 10/Full Duplex" );
}
}
else {
fSpeed100 = true;
if ( (modeControl & R_BMC_DUPLEXMODE) == 0 )
{
activeMediumIndex = MEDIUM_INDEX_TX_HD; ELG( currentMediumIndex, activeMediumIndex, 'TXHD', "RTL8139::phyReportLinkStatus - 100/half" );
}
else if ( anlp & MII_ANLP_PAUSE
&& (currentMediumIndex == MEDIUM_INDEX_AUTO
|| currentMediumIndex == MEDIUM_INDEX_TX_FC) )
{
activeMediumIndex = MEDIUM_INDEX_TX_FC; ELG( currentMediumIndex, activeMediumIndex, 'TXFC', "RTL8139::phyReportLinkStatus - 100/Flow Control" );
}
else
{
activeMediumIndex = MEDIUM_INDEX_TX_FD; ELG( currentMediumIndex, activeMediumIndex, 'TXFD', "RTL8139::phyReportLinkStatus - 100/Full Duplex" );
}
}
setLinkStatus( kIONetworkLinkValid | kIONetworkLinkActive,
phyGetMediumWithIndex( activeMediumIndex ),
fSpeed100 ? 100000000 : 10000000 );
enableHardwareInterrupts();
fTransmitQueue->start();
}
else {
setLinkStatus( kIONetworkLinkValid ); disableHardwareInterrupts();
fTransmitQueue->stop();
fTransmitQueue->flush();
}
phyStatusLast = phyStatus; }
forceLinkChange = false; return;
}
const IONetworkMedium* RTL8139::phyGetMediumWithIndex( MediumIndex index ) const
{
if ( index < MEDIUM_INDEX_COUNT )
return mediumTable[ index ];
return 0;
}