/* ---------------------------------------------------------------------------- | |
* 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 "chip.h" | |
#include <stdio.h> | |
#include <string.h> | |
#include <assert.h> | |
/*---------------------------------------------------------------------------- | |
* Definitions | |
*----------------------------------------------------------------------------*/ | |
/** The buffer addresses written into the descriptors must be aligned so the | |
last few bits are zero. These bits have special meaning for the GMAC | |
peripheral and cannot be used as part of the address.*/ | |
#define GMAC_ADDRESS_MASK ((uint32_t)0xFFFFFFFC) | |
#define GMAC_LENGTH_FRAME ((uint32_t)0x3FFF) /** Length of frame mask */ | |
/** receive buffer descriptor bits */ | |
#define GMAC_RX_OWNERSHIP_BIT (1 << 0) | |
#define GMAC_RX_WRAP_BIT (1 << 1) | |
#define GMAC_RX_SOF_BIT (1 << 14) | |
#define GMAC_RX_EOF_BIT (1 << 15) | |
/** Transmit buffer descriptor bits */ | |
#define GMAC_TX_LAST_BUFFER_BIT (1 << 15) | |
#define GMAC_TX_WRAP_BIT (1 << 30) | |
#define GMAC_TX_USED_BIT (1 << 31) | |
#define GMAC_TX_RLE_BIT (1 << 29) /** Retry Limit Exceeded */ | |
#define GMAC_TX_UND_BIT (1 << 28) /** Tx Buffer Underrun */ | |
#define GMAC_TX_ERR_BIT (1 << 27) /** Exhausted in mid-frame */ | |
#define GMAC_TX_ERR_BITS \ | |
(GMAC_TX_RLE_BIT | GMAC_TX_UND_BIT | GMAC_TX_ERR_BIT) | |
/*---------------------------------------------------------------------------- | |
* Internal functions | |
*----------------------------------------------------------------------------*/ | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* Return 1 if PHY is idle | |
*/ | |
uint8_t GMAC_IsIdle(Gmac *pGmac) | |
{ | |
return ((pGmac->GMAC_NSR & GMAC_NSR_IDLE) > 0); | |
} | |
/** | |
* Execute PHY maintanance command | |
*/ | |
void GMAC_PHYMaintain(Gmac *pGmac, | |
uint8_t bPhyAddr, | |
uint8_t bRegAddr, | |
uint8_t bRW, | |
uint16_t wData) | |
{ | |
/* Wait until bus idle */ | |
while((pGmac->GMAC_NSR & GMAC_NSR_IDLE) == 0); | |
/* Write maintain register */ | |
pGmac->GMAC_MAN = (~GMAC_MAN_WZO & GMAC_MAN_CLTTO) | |
| (GMAC_MAN_OP(bRW ? 0x2 : 0x1)) | |
| GMAC_MAN_WTN(0x02) | |
| GMAC_MAN_PHYA(bPhyAddr) | |
| GMAC_MAN_REGA(bRegAddr) | |
| GMAC_MAN_DATA(wData) ; | |
} | |
/** | |
* Return PHY maintainance data returned | |
*/ | |
uint16_t GMAC_PHYData(Gmac *pGmac) | |
{ | |
/* Wait until bus idle */ | |
while((pGmac->GMAC_NSR & GMAC_NSR_IDLE) == 0); | |
/* Return data */ | |
return (uint16_t)(pGmac->GMAC_MAN & GMAC_MAN_DATA_Msk); | |
} | |
/** | |
* \brief Set MDC clock according to current board clock. Per 802.3, MDC should be | |
* less then 2.5MHz. | |
* \param pGmac Pointer to an Gmac instance. | |
* \param mck Mdc clock | |
* \return 1 if successfully, 0 if MDC clock not found. | |
*/ | |
uint8_t GMAC_SetMdcClock( Gmac *pGmac, uint32_t mck ) | |
{ | |
uint32_t clock_dividor; | |
pGmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
if (mck <= 20000000) { | |
clock_dividor = GMAC_NCFGR_CLK_MCK_8; // MDC clock = MCK/8 | |
} | |
else if (mck <= 40000000) { | |
clock_dividor = GMAC_NCFGR_CLK_MCK_16; // MDC clock = MCK/16 | |
} | |
else if (mck <= 80000000) { | |
clock_dividor = GMAC_NCFGR_CLK_MCK_32; // MDC clock = MCK/32 | |
} | |
else if (mck <= 160000000) { | |
clock_dividor = GMAC_NCFGR_CLK_MCK_64; // MDC clock = MCK/64 | |
} | |
else if (mck <= 240000000) { | |
clock_dividor = GMAC_NCFGR_CLK_MCK_96; // MDC clock = MCK/96 | |
} | |
else { | |
TRACE_ERROR("E: No valid MDC clock.\n\r"); | |
return 0; | |
} | |
pGmac->GMAC_NCFGR = (pGmac->GMAC_NCFGR & (~GMAC_NCFGR_CLK_Msk)) | clock_dividor; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
return 1; | |
} | |
/** | |
* \brief Enable MDI with PHY | |
* \param pGmac Pointer to an Gmac instance. | |
*/ | |
void GMAC_EnableMdio( Gmac *pGmac ) | |
{ | |
pGmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
pGmac->GMAC_NCR |= GMAC_NCR_MPE; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
} | |
/** | |
* \brief Enable MDI with PHY | |
* \param pGmac Pointer to an Gmac instance. | |
*/ | |
void GMAC_DisableMdio( Gmac *pGmac ) | |
{ | |
pGmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
pGmac->GMAC_NCR &= ~GMAC_NCR_MPE; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
} | |
/** | |
* \brief Enable MII mode for GMAC, called once after autonegotiate | |
* \param pGmac Pointer to an Gmac instance. | |
*/ | |
void GMAC_EnableMII( Gmac *pGmac ) | |
{ | |
pGmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
pGmac->GMAC_UR &= ~GMAC_UR_RMII; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
} | |
/** | |
* \brief Enable GMII mode for GMAC, called once after autonegotiate | |
* \param pGmac Pointer to an Gmac instance. | |
*/ | |
void GMAC_EnableGMII( Gmac *pGmac ) | |
{ | |
pGmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
/* RGMII disable */ | |
pGmac->GMAC_UR &= ~GMAC_UR_RMII; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
} | |
#define GMAC_NCFGR_GBE (0x1u << 10) | |
/** | |
* \brief Enable RGMII mode for GMAC, called once after autonegotiate | |
* \param pGmac Pointer to an Gmac instance. | |
* \param duplex: 1 full duplex 0 half duplex | |
* \param speed: 0 10M 1 100M | |
*/ | |
void GMAC_EnableRGMII(Gmac *pGmac, uint32_t duplex, uint32_t speed) | |
{ | |
pGmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
if (duplex == GMAC_DUPLEX_HALF) | |
{ | |
pGmac->GMAC_NCFGR &= ~GMAC_NCFGR_FD; | |
} | |
else | |
{ | |
pGmac->GMAC_NCFGR |= GMAC_NCFGR_FD; | |
} | |
if (speed == GMAC_SPEED_10M) | |
{ | |
pGmac->GMAC_NCFGR &= ~GMAC_NCFGR_SPD; | |
} | |
else if(speed == GMAC_SPEED_100M) | |
{ | |
pGmac->GMAC_NCFGR |= GMAC_NCFGR_SPD; | |
} | |
else | |
{ | |
pGmac->GMAC_NCFGR |= GMAC_NCFGR_SPD; | |
} | |
/* RGMII enable */ | |
pGmac->GMAC_UR |= GMAC_UR_RMII; | |
pGmac->GMAC_NCFGR &= ~GMAC_NCFGR_GBE; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
return; | |
} | |
/** | |
* \brief Setup the GMAC for the link : speed 100M/10M and Full/Half duplex | |
* \param pGmac Pointer to an Gmac instance. | |
* \param speed Link speed, 0 for 10M, 1 for 100M | |
* \param fullduplex 1 for Full Duplex mode | |
*/ | |
void GMAC_SetLinkSpeed(Gmac *pGmac, uint8_t speed, uint8_t fullduplex) | |
{ | |
uint32_t ncfgr; | |
ncfgr = pGmac->GMAC_NCFGR; | |
ncfgr &= ~(GMAC_NCFGR_SPD | GMAC_NCFGR_FD); | |
if (speed) { | |
ncfgr |= GMAC_NCFGR_SPD; | |
} | |
if (fullduplex) { | |
ncfgr |= GMAC_NCFGR_FD; | |
} | |
pGmac->GMAC_NCFGR = ncfgr; | |
pGmac->GMAC_NCR |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN); | |
} | |
/** | |
* \brief set local loop back | |
* \param pGmac Pointer to an Gmac instance. | |
*/ | |
uint32_t GMAC_SetLocalLoopBack(Gmac *pGmac) | |
{ | |
pGmac->GMAC_NCR |= GMAC_NCR_LBL; | |
return 0; | |
} | |
/** | |
* Return interrupt mask. | |
*/ | |
uint32_t GMAC_GetItMask(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_IMR; | |
} | |
/** | |
* Return transmit status | |
*/ | |
uint32_t GMAC_GetTxStatus(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_TSR; | |
} | |
/** | |
* Clear transmit status | |
*/ | |
void GMAC_ClearTxStatus(Gmac *pGmac, uint32_t dwStatus) | |
{ | |
pGmac->GMAC_TSR = dwStatus; | |
} | |
/** | |
* Return receive status | |
*/ | |
uint32_t GMAC_GetRxStatus(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_RSR; | |
} | |
/** | |
* Clear receive status | |
*/ | |
void GMAC_ClearRxStatus(Gmac *pGmac, uint32_t dwStatus) | |
{ | |
pGmac->GMAC_RSR = dwStatus; | |
} | |
/** | |
* Enable/Disable GMAC receive. | |
*/ | |
void GMAC_ReceiveEnable(Gmac* pGmac, uint8_t bEnaDis) | |
{ | |
if (bEnaDis) pGmac->GMAC_NCR |= GMAC_NCR_RXEN; | |
else pGmac->GMAC_NCR &= ~GMAC_NCR_RXEN; | |
} | |
/** | |
* Enable/Disable GMAC transmit. | |
*/ | |
void GMAC_TransmitEnable(Gmac *pGmac, uint8_t bEnaDis) | |
{ | |
if (bEnaDis) pGmac->GMAC_NCR |= GMAC_NCR_TXEN; | |
else pGmac->GMAC_NCR &= ~GMAC_NCR_TXEN; | |
} | |
/** | |
* Set Rx Queue | |
*/ | |
void GMAC_SetRxQueue(Gmac *pGmac, uint32_t dwAddr) | |
{ | |
pGmac->GMAC_RBQB = GMAC_RBQB_ADDR_Msk & dwAddr; | |
} | |
/** | |
* Get Rx Queue Address | |
*/ | |
uint32_t GMAC_GetRxQueue(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_RBQB; | |
} | |
/** | |
* Set Tx Queue | |
*/ | |
void GMAC_SetTxQueue(Gmac *pGmac, uint32_t dwAddr) | |
{ | |
pGmac->GMAC_TBQB = GMAC_TBQB_ADDR_Msk & dwAddr; | |
} | |
/** | |
* Get Tx Queue | |
*/ | |
uint32_t GMAC_GetTxQueue(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_TBQB; | |
} | |
/** | |
* Write control value | |
*/ | |
void GMAC_NetworkControl(Gmac *pGmac, uint32_t bmNCR) | |
{ | |
pGmac->GMAC_NCR = bmNCR; | |
} | |
/** | |
* Get control value | |
*/ | |
uint32_t GMAC_GetNetworkControl(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_NCR; | |
} | |
/** | |
* Enable interrupt(s). | |
*/ | |
void GMAC_EnableIt(Gmac *pGmac, uint32_t dwSources) | |
{ | |
pGmac->GMAC_IER = dwSources; | |
} | |
/** | |
* Disable interrupt(s). | |
*/ | |
void GMAC_DisableIt(Gmac *pGmac, uint32_t dwSources) | |
{ | |
pGmac->GMAC_IDR = dwSources; | |
} | |
/** | |
* Return interrupt status. | |
*/ | |
uint32_t GMAC_GetItStatus(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_ISR; | |
} | |
/** | |
* Set MAC Address | |
*/ | |
void GMAC_SetAddress(Gmac *pGmac, uint8_t bIndex, uint8_t *pMacAddr) | |
{ | |
pGmac->GMAC_SA[bIndex].GMAC_SAB = (pMacAddr[3] << 24) | |
| (pMacAddr[2] << 16) | |
| (pMacAddr[1] << 8) | |
| (pMacAddr[0] ) | |
; | |
pGmac->GMAC_SA[bIndex].GMAC_SAT = (pMacAddr[5] << 8) | |
| (pMacAddr[4] ) | |
; | |
} | |
/** | |
* Set MAC Address via 2 DW | |
*/ | |
void GMAC_SetAddress32(Gmac *pGmac, uint8_t bIndex, uint32_t dwMacT, uint32_t dwMacB) | |
{ | |
pGmac->GMAC_SA[bIndex].GMAC_SAB = dwMacB; | |
pGmac->GMAC_SA[bIndex].GMAC_SAT = dwMacT; | |
} | |
/** | |
* Set MAC Address via int64 | |
*/ | |
void GMAC_SetAddress64(Gmac *pGmac, uint8_t bIndex, uint64_t ddwMac) | |
{ | |
pGmac->GMAC_SA[bIndex].GMAC_SAB = (uint32_t)ddwMac; | |
pGmac->GMAC_SA[bIndex].GMAC_SAT = (uint32_t)(ddwMac >> 32); | |
} | |
/** | |
* Clear all statistics registers | |
*/ | |
void GMAC_ClearStatistics(Gmac *pGmac) | |
{ | |
pGmac->GMAC_NCR |= GMAC_NCR_CLRSTAT; | |
} | |
/** | |
* Increase all statistics registers | |
*/ | |
void GMAC_IncreaseStatistics(Gmac *pGmac) | |
{ | |
pGmac->GMAC_NCR |= GMAC_NCR_INCSTAT; | |
} | |
/** | |
* Enable/Disable statistics registers writing. | |
*/ | |
void GMAC_StatisticsWriteEnable(Gmac *pGmac, uint8_t bEnaDis) | |
{ | |
if (bEnaDis) pGmac->GMAC_NCR |= GMAC_NCR_WESTAT; | |
else pGmac->GMAC_NCR &= ~GMAC_NCR_WESTAT; | |
} | |
/** | |
* Setup network configuration register | |
*/ | |
void GMAC_Configure(Gmac *pGmac, uint32_t dwCfg) | |
{ | |
pGmac->GMAC_NCFGR = dwCfg; | |
} | |
/** | |
* Return network configuration. | |
*/ | |
uint32_t GMAC_GetConfigure(Gmac *pGmac) | |
{ | |
return pGmac->GMAC_NCFGR; | |
} | |
/** | |
* Start transmission | |
*/ | |
void GMAC_TransmissionStart(Gmac *pGmac) | |
{ | |
pGmac->GMAC_NCR |= GMAC_NCR_TSTART; | |
} | |
/** | |
* Halt transmission | |
*/ | |
void GMAC_TransmissionHalt(Gmac *pGmac) | |
{ | |
pGmac->GMAC_NCR |= GMAC_NCR_THALT; | |
} |