/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2013, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \file */ | |
/*--------------------------------------------------------------------------- | |
* Headers | |
*---------------------------------------------------------------------------*/ | |
#include <board.h> | |
/*--------------------------------------------------------------------------- | |
* Definitions | |
*---------------------------------------------------------------------------*/ | |
/** Default max retry count */ | |
#define GMACB_RETRY_MAX 300000 | |
/** Default max retry count */ | |
#define GACB_RETRY_MAX 1000000 | |
/*--------------------------------------------------------------------------- | |
* Local functions | |
*---------------------------------------------------------------------------*/ | |
/** | |
* Wait PHY operation complete. | |
* Return 1 if the operation completed successfully. | |
* May be need to re-implemented to reduce CPU load. | |
* \param retry: the retry times, 0 to wait forever until complete. | |
*/ | |
static uint8_t GMACB_WaitPhy( Gmac *pHw, uint32_t retry ) | |
{ | |
volatile uint32_t retry_count = 0; | |
while (!GMAC_IsIdle(pHw)) | |
{ | |
if(retry == 0) continue; | |
retry_count ++; | |
if (retry_count >= retry) | |
{ | |
return 0; | |
} | |
} | |
return 1; | |
} | |
/** | |
* Read PHY register. | |
* Return 1 if successfully, 0 if timeout. | |
* \param pHw HW controller address | |
* \param PhyAddress PHY Address | |
* \param Address Register Address | |
* \param pValue Pointer to a 32 bit location to store read data | |
* \param retry The retry times, 0 to wait forever until complete. | |
*/ | |
static uint8_t GMACB_ReadPhy(Gmac *pHw, | |
uint8_t PhyAddress, | |
uint8_t Address, | |
uint32_t *pValue, | |
uint32_t retry) | |
{ | |
GMAC_PHYMaintain(pHw, PhyAddress, Address, 1, 0); | |
if ( GMACB_WaitPhy(pHw, retry) == 0 ) | |
{ | |
TRACE_ERROR("TimeOut GMACB_ReadPhy\n\r"); | |
return 0; | |
} | |
*pValue = GMAC_PHYData(pHw); | |
return 1; | |
} | |
/** | |
* Write PHY register | |
* Return 1 if successfully, 0 if timeout. | |
* \param pHw HW controller address | |
* \param PhyAddress PHY Address | |
* \param Address Register Address | |
* \param Value Data to write ( Actually 16 bit data ) | |
* \param retry The retry times, 0 to wait forever until complete. | |
*/ | |
static uint8_t GMACB_WritePhy(Gmac *pHw, | |
uint8_t PhyAddress, | |
uint8_t Address, | |
uint32_t Value, | |
uint32_t retry) | |
{ | |
GMAC_PHYMaintain(pHw, PhyAddress, Address, 0, Value); | |
if ( GMACB_WaitPhy(pHw, retry) == 0 ) | |
{ | |
TRACE_ERROR("TimeOut GMACB_WritePhy\n\r"); | |
return 0; | |
} | |
return 1; | |
} | |
/*--------------------------------------------------------------------------- | |
* Exported functions | |
*---------------------------------------------------------------------------*/ | |
/** | |
* \brief Find a valid PHY Address ( from 0 to 31 ). | |
* \param pMacb Pointer to the MACB instance | |
* \return 0xFF when no valid PHY Address found. | |
*/ | |
static uint8_t GMACB_FindValidPhy(GMacb *pMacb) | |
{ | |
sGmacd *pDrv = pMacb->pGmacd; | |
Gmac *pHw = pDrv->pHw; | |
uint32_t retryMax; | |
uint32_t value=0; | |
uint8_t rc; | |
uint8_t phyAddress; | |
uint8_t cnt; | |
TRACE_DEBUG("GMACB_FindValidPhy\n\r"); | |
GMAC_EnableMdio(pHw); | |
phyAddress = pMacb->phyAddress; | |
retryMax = pMacb->retryMax; | |
/* Check current phyAddress */ | |
rc = phyAddress; | |
if( GMACB_ReadPhy(pHw, phyAddress, GMII_PHYID1R, &value, retryMax) == 0 ) { | |
TRACE_ERROR("GMACB PROBLEM\n\r"); | |
} | |
TRACE_DEBUG("_PHYID1 : 0x%X, addr: %d\n\r", value, phyAddress); | |
/* Find another one */ | |
if (value != GMII_OUI_MSB) { | |
rc = 0xFF; | |
for(cnt = 0; cnt < 32; cnt ++) { | |
phyAddress = (phyAddress + 1) & 0x1F; | |
if( GMACB_ReadPhy(pHw, phyAddress, GMII_PHYID1R, &value, retryMax) == 0 ) { | |
TRACE_ERROR("MACB PROBLEM\n\r"); | |
} | |
TRACE_DEBUG("_PHYID1 : 0x%X, addr: %d\n\r", value, phyAddress); | |
if (value == GMII_OUI_MSB) { | |
rc = phyAddress; | |
break; | |
} | |
} | |
} | |
if (rc != 0xFF) { | |
TRACE_INFO("** Valid PHY Found: %d\n\r", rc); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_PHYID1R, &value, retryMax); | |
TRACE_DEBUG("_PHYID1R : 0x%X, addr: %d\n\r", value, phyAddress); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_PHYID2R, &value, retryMax); | |
TRACE_DEBUG("_EMSR : 0x%X, addr: %d\n\r", value, phyAddress); | |
} | |
GMAC_DisableMdio(pHw); | |
return rc; | |
} | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Dump all the useful registers. | |
* \param pMacb Pointer to the MACB instance | |
*/ | |
void GMACB_DumpRegisters(GMacb *pMacb) | |
{ | |
sGmacd *pDrv = pMacb->pGmacd; | |
Gmac *pHw = pDrv->pHw; | |
uint8_t phyAddress; | |
uint32_t retryMax; | |
uint32_t value; | |
TRACE_INFO("GMACB_DumpRegisters\n\r"); | |
GMAC_EnableMdio(pHw); | |
phyAddress = pMacb->phyAddress; | |
retryMax = pMacb->retryMax; | |
TRACE_INFO("GMII MACB @%d) Registers:\n\r", phyAddress); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_BMCR, &value, retryMax); | |
TRACE_INFO(" _BMCR : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_BMSR, &value, retryMax); | |
TRACE_INFO(" _BMSR : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_ANAR, &value, retryMax); | |
TRACE_INFO(" _ANAR : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_ANLPAR, &value, retryMax); | |
TRACE_INFO(" _ANLPAR : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_ANER, &value, retryMax); | |
TRACE_INFO(" _ANER : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_ANNPR, &value, retryMax); | |
TRACE_INFO(" _ANNPR : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_ANLPNPAR, &value, retryMax); | |
TRACE_INFO(" _ANLPNPAR : 0x%X\n\r", value); | |
TRACE_INFO(" \n\r"); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_RXERCR, &value, retryMax); | |
TRACE_INFO(" _RXERCR : 0x%X\n\r", value); | |
GMACB_ReadPhy(pHw, phyAddress, GMII_ICSR, &value, retryMax); | |
TRACE_INFO(" _ICSR : 0x%X\n\r", value); | |
TRACE_INFO(" \n\r"); | |
GMAC_DisableMdio(pHw); | |
} | |
/** | |
* \brief Setup the maximum timeout count of the driver. | |
* \param pMacb Pointer to the MACB instance | |
* \param toMax Timeout maxmum count. | |
*/ | |
void GMACB_SetupTimeout(GMacb *pMacb, uint32_t toMax) | |
{ | |
pMacb->retryMax = toMax; | |
} | |
/** | |
* \brief Initialize the MACB instance. | |
* \param pMacb Pointer to the MACB instance | |
* \param phyAddress The PHY address used to access the PHY | |
*/ | |
void GMACB_Init(GMacb *pMacb, sGmacd *pGmacd, uint8_t phyAddress) | |
{ | |
pMacb->pGmacd = pGmacd; | |
pMacb->phyAddress = phyAddress; | |
/* Initialize timeout by default */ | |
pMacb->retryMax = GMACB_RETRY_MAX; | |
} | |
/** | |
* \brief Issue a SW reset to reset all registers of the PHY. | |
* \param pMacb Pointer to the MACB instance | |
* \return 1 if successfully, 0 if timeout. | |
*/ | |
uint8_t GMACB_ResetPhy(GMacb *pMacb) | |
{ | |
sGmacd *pDrv = pMacb->pGmacd; | |
Gmac *pHw = pDrv->pHw; | |
uint32_t retryMax; | |
uint32_t bmcr = GMII_RESET; | |
uint8_t phyAddress; | |
uint32_t timeout = 10; | |
uint8_t ret = 1; | |
TRACE_INFO(" GMACB_ResetPhy\n\r"); | |
phyAddress = pMacb->phyAddress; | |
retryMax = pMacb->retryMax; | |
GMAC_EnableMdio(pHw); | |
bmcr = GMII_RESET; | |
GMACB_WritePhy(pHw, phyAddress, GMII_BMCR, bmcr, retryMax); | |
do { | |
GMACB_ReadPhy(pHw, phyAddress, GMII_BMCR, &bmcr, retryMax); | |
timeout--; | |
} while ((bmcr & GMII_RESET) && timeout); | |
GMAC_DisableMdio(pHw); | |
if (!timeout) { | |
ret = 0; | |
} | |
return( ret ); | |
} | |
/** | |
* \brief Do a HW initialize to the PHY ( via RSTC ) and setup clocks & PIOs | |
* This should be called only once to initialize the PHY pre-settings. | |
* The PHY address is reset status of CRS,RXD[3:0] (the emacPins' pullups). | |
* The COL pin is used to select MII mode on reset (pulled up for Reduced MII) | |
* The RXDV pin is used to select test mode on reset (pulled up for test mode) | |
* The above pins should be predefined for corresponding settings in resetPins | |
* The GMAC peripheral pins are configured after the reset done. | |
* \param pMacb Pointer to the MACB instance | |
* \param mck Main clock setting to initialize clock | |
* \param resetPins Pointer to list of PIOs to configure before HW RESET | |
* (for PHY power on reset configuration latch) | |
* \param nbResetPins Number of PIO items that should be configured | |
* \param emacPins Pointer to list of PIOs for the EMAC interface | |
* \param nbEmacPins Number of PIO items that should be configured | |
* \return 1 if RESET OK, 0 if timeout. | |
*/ | |
uint8_t GMACB_InitPhy(GMacb *pMacb, | |
uint32_t mck, | |
const Pin *pResetPins, | |
uint32_t nbResetPins, | |
const Pin *pGmacPins, | |
uint32_t nbGmacPins) | |
{ | |
sGmacd *pDrv = pMacb->pGmacd; | |
Gmac *pHw = pDrv->pHw; | |
uint8_t rc = 1; | |
uint8_t phy; | |
/* Perform RESET */ | |
TRACE_DEBUG("RESET PHY\n\r"); | |
if (pResetPins) { | |
/* Configure PINS */ | |
PIO_Configure(pResetPins, nbResetPins); | |
} | |
/* Configure GMAC runtime pins */ | |
if (rc) { | |
PIO_Configure(pGmacPins, nbGmacPins); | |
rc = GMAC_SetMdcClock(pHw, mck ); | |
if (!rc) { | |
TRACE_ERROR("No Valid MDC clock\n\r"); | |
return 0; | |
} | |
/* Check PHY Address */ | |
phy = GMACB_FindValidPhy(pMacb); | |
if (phy == 0xFF) { | |
TRACE_ERROR("PHY Access fail\n\r"); | |
return 0; | |
} | |
if(phy != pMacb->phyAddress) { | |
pMacb->phyAddress = phy; | |
GMACB_ResetPhy(pMacb); | |
} | |
} | |
else { | |
TRACE_ERROR("PHY Reset Timeout\n\r"); | |
} | |
return rc; | |
} | |
/** | |
* \brief Issue a Auto Negotiation of the PHY | |
* \param pMacb Pointer to the MACB instance | |
* \return 1 if successfully, 0 if timeout. | |
*/ | |
uint8_t GMACB_AutoNegotiate(GMacb *pMacb) | |
{ | |
sGmacd *pDrv = pMacb->pGmacd; | |
Gmac *pHw = pDrv->pHw; | |
uint32_t retryMax; | |
uint32_t value; | |
uint32_t phyAnar; | |
uint32_t phyAnalpar; | |
uint32_t retryCount= 0; | |
uint8_t phyAddress; | |
uint8_t rc = 1; | |
uint32_t duplex, speed; | |
phyAddress = pMacb->phyAddress; | |
retryMax = pMacb->retryMax; | |
GMAC_EnableMdio(pHw); | |
if (!GMACB_ReadPhy(pHw,phyAddress, GMII_PHYID1R, &value, retryMax)) | |
{ | |
TRACE_ERROR("Pb GEMAC_ReadPhy Id1\n\r"); | |
rc = 0; | |
goto AutoNegotiateExit; | |
} | |
TRACE_DEBUG("ReadPhy Id1 0x%X, addresse: %d\n\r", value, phyAddress); | |
if (!GMACB_ReadPhy(pHw,phyAddress, GMII_PHYID2R, &phyAnar, retryMax)) | |
{ | |
TRACE_ERROR("Pb GMACB_ReadPhy Id2\n\r"); | |
rc = 0; | |
goto AutoNegotiateExit; | |
} | |
TRACE_DEBUG("ReadPhy Id2 0x%X\n\r", phyAnar); | |
if( ( value == GMII_OUI_MSB ) | |
&& ( ((phyAnar)&(~GMII_LSB_MASK)) == GMII_OUI_LSB ) ) | |
{ | |
TRACE_DEBUG("Vendor Number Model = 0x%X\n\r", ((phyAnar>>4)&0x3F)); | |
TRACE_DEBUG("Model Revision Number = 0x%X\n\r", (phyAnar&0xF)); | |
} | |
else | |
{ | |
TRACE_ERROR("Problem OUI value\n\r"); | |
} | |
/* Set the Auto_negotiation Advertisement Register, MII advertising for Next page | |
100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3 */ | |
rc = GMACB_ReadPhy(pHw, phyAddress, GMII_ANAR, &phyAnar, retryMax); | |
if (rc == 0) | |
{ | |
goto AutoNegotiateExit; | |
} | |
phyAnar = GMII_TX_FDX | GMII_TX_HDX | | |
GMII_10_FDX | GMII_10_HDX | GMII_AN_IEEE_802_3; | |
rc = GMACB_WritePhy(pHw,phyAddress, GMII_ANAR, phyAnar, retryMax); | |
if (rc == 0) | |
{ | |
goto AutoNegotiateExit; | |
} | |
/* Read & modify control register */ | |
rc = GMACB_ReadPhy(pHw, phyAddress, GMII_BMCR, &value, retryMax); | |
if (rc == 0) | |
{ | |
goto AutoNegotiateExit; | |
} | |
/* Check AutoNegotiate complete */ | |
value |= GMII_AUTONEG | GMII_RESTART_AUTONEG; | |
rc = GMACB_WritePhy(pHw, phyAddress, GMII_BMCR, value, retryMax); | |
if (rc == 0) | |
{ | |
goto AutoNegotiateExit; | |
} | |
TRACE_DEBUG(" _BMCR: 0x%X\n\r", value); | |
// Check AutoNegotiate complete | |
while (1) | |
{ | |
rc = GMACB_ReadPhy(pHw, phyAddress, GMII_BMSR, &value, retryMax); | |
if (rc == 0) | |
{ | |
TRACE_ERROR("rc==0\n\r"); | |
goto AutoNegotiateExit; | |
} | |
/* Done successfully */ | |
if (value & GMII_AUTONEG_COMP) | |
{ | |
printf("AutoNegotiate complete\n\r"); | |
break; | |
} | |
/* Timeout check */ | |
if (retryMax) | |
{ | |
if (++ retryCount >= retryMax) | |
{ | |
GMACB_DumpRegisters(pMacb); | |
TRACE_ERROR("TimeOut\n\r"); | |
rc = 0; | |
goto AutoNegotiateExit; | |
} | |
} | |
} | |
/*Set local link mode */ | |
while(1) | |
{ | |
rc = GMACB_ReadPhy(pHw, phyAddress, GMII_ANLPAR, &phyAnalpar, retryMax); | |
if (rc == 0) | |
{ | |
goto AutoNegotiateExit; | |
} | |
/* Setup the GMAC link speed */ | |
if ((phyAnar & phyAnalpar) & GMII_TX_FDX) | |
{ | |
/* set RGMII for 1000BaseTX and Full Duplex */ | |
duplex = GMAC_DUPLEX_FULL; | |
speed = GMAC_SPEED_100M; | |
break; | |
} | |
else if ((phyAnar & phyAnalpar) & GMII_10_FDX) | |
{ | |
/* set RGMII for 1000BaseT and Half Duplex*/ | |
duplex = GMAC_DUPLEX_FULL; | |
speed = GMAC_SPEED_10M; | |
break; | |
} | |
else if ((phyAnar & phyAnalpar) & GMII_TX_HDX) | |
{ | |
/* set RGMII for 100BaseTX and half Duplex */ | |
duplex = GMAC_DUPLEX_HALF; | |
speed = GMAC_SPEED_100M; | |
break; | |
} | |
else if ((phyAnar & phyAnalpar) & GMII_10_HDX) | |
{ | |
// set RGMII for 10BaseT and half Duplex | |
duplex = GMAC_DUPLEX_HALF; | |
speed = GMAC_SPEED_10M; | |
break; | |
} | |
} | |
TRACE_INFO("-I- GMAC_EnableRGMII duplex %d, speed %d\n\r",duplex,speed); | |
/* Setup GMAC mode */ | |
GMAC_EnableRGMII(pHw, duplex, speed); | |
AutoNegotiateExit: | |
GMAC_DisableMdio(pHw); | |
return rc; | |
} |