1044 lines
35 KiB
C
1044 lines
35 KiB
C
/**********************************************************************
|
|
* $Id$ lpc_emac.c 2011-06-02
|
|
*//**
|
|
* @file lpc_emac.c
|
|
* @brief Contains all functions support for Ethernet MAC firmware
|
|
* library on LPC
|
|
* @version 1.0
|
|
* @date 02. June. 2011
|
|
* @author NXP MCU SW Application Team
|
|
*
|
|
* Copyright(C) 2011, NXP Semiconductor
|
|
* All rights reserved.
|
|
*
|
|
***********************************************************************
|
|
* Software that is described herein is for illustrative purposes only
|
|
* which provides customers with programming information regarding the
|
|
* products. This software is supplied "AS IS" without any warranties.
|
|
* NXP Semiconductors assumes no responsibility or liability for the
|
|
* use of the software, conveys no license or title under any patent,
|
|
* copyright, or mask work right to the product. NXP Semiconductors
|
|
* reserves the right to make changes in the software without
|
|
* notification. NXP Semiconductors also make no representation or
|
|
* warranty that such application will be suitable for the specified
|
|
* use without further testing or modification.
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation is hereby granted, under NXP Semiconductors'
|
|
* relevant copyright in the software, without fee, provided that it
|
|
* is used in conjunction with NXP Semiconductors microcontrollers. This
|
|
* copyright, permission, and disclaimer notice must appear in all copies of
|
|
* this code.
|
|
**********************************************************************/
|
|
#ifdef __BUILD_WITH_EXAMPLE__
|
|
#include "lpc_libcfg.h"
|
|
#else
|
|
#include "lpc_libcfg_default.h"
|
|
#endif /* __BUILD_WITH_EXAMPLE__ */
|
|
#ifdef _EMAC
|
|
|
|
#include "lpc_emac.h"
|
|
#include "lpc_clkpwr.h"
|
|
#include "lpc_pinsel.h"
|
|
|
|
/* Peripheral group ----------------------------------------------------------- */
|
|
/** @addtogroup EMAC
|
|
* @{
|
|
*/
|
|
|
|
/************************** PRIVATE VARIABLES *************************/
|
|
|
|
|
|
/* MII Mgmt Configuration register - Clock divider setting */
|
|
const uint8_t EMAC_clkdiv[] = { 4, 6, 8, 10, 14, 20, 28, 36, 40, 44, 48, 52, 56, 60, 64 };
|
|
|
|
/* EMAC Config data */
|
|
static EMAC_CFG_Type EMAC_Configs;
|
|
|
|
|
|
/* EMAC local DMA Descriptors */
|
|
|
|
|
|
#ifdef __IAR_SYSTEMS_ICC__
|
|
/* Global Tx Buffer data */
|
|
#pragma data_alignment=4
|
|
static uint16_t saFrameBuffers[EMAC_MAX_FRAME_NUM][EMAC_MAX_FRAME_SIZE];
|
|
#else
|
|
/* Global Rx Buffer data */
|
|
static uint16_t __attribute__ ((aligned (4))) saFrameBuffers[EMAC_MAX_FRAME_NUM][EMAC_MAX_FRAME_SIZE];
|
|
#endif
|
|
static uint32_t sulCurrFrameSz = 0;
|
|
static uint8_t sbCurrFrameID = 0;
|
|
|
|
/***************************** PRIVATE FUNCTION *****************************/
|
|
static void EMAC_UpdateRxConsumeIndex(void);
|
|
static void EMAC_UpdateTxProduceIndex(void);
|
|
static uint32_t EMAC_AllocTxBuff(uint16_t nFrameSize, uint8_t bLastFrame);
|
|
static uint32_t EMAC_GetRxFrameSize(void);
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief
|
|
* @param[in]
|
|
* @return
|
|
**********************************************************************/
|
|
void rx_descr_init (void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < EMAC_NUM_RX_FRAG; i++)
|
|
{
|
|
RX_DESC_PACKET(i) = RX_BUF(i);
|
|
RX_DESC_CTRL(i) = EMAC_RCTRL_INT | (EMAC_ETH_MAX_FLEN-1);
|
|
RX_STAT_INFO(i) = 0;
|
|
RX_STAT_HASHCRC(i) = 0;
|
|
}
|
|
|
|
/* Set EMAC Receive Descriptor Registers. */
|
|
LPC_EMAC->RxDescriptor = RX_DESC_BASE;
|
|
LPC_EMAC->RxStatus = RX_STAT_BASE;
|
|
LPC_EMAC->RxDescriptorNumber = EMAC_NUM_RX_FRAG-1;
|
|
|
|
/* Rx Descriptors Point to 0 */
|
|
LPC_EMAC->RxConsumeIndex = 0;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief
|
|
* @param[in]
|
|
* @return
|
|
**********************************************************************/
|
|
void tx_descr_init (void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < EMAC_NUM_TX_FRAG; i++)
|
|
{
|
|
TX_DESC_PACKET(i) = TX_BUF(i);
|
|
TX_DESC_CTRL(i) = 0;
|
|
TX_STAT_INFO(i) = 0;
|
|
}
|
|
|
|
/* Set EMAC Transmit Descriptor Registers. */
|
|
LPC_EMAC->TxDescriptor = TX_DESC_BASE;
|
|
LPC_EMAC->TxStatus = TX_STAT_BASE;
|
|
LPC_EMAC->TxDescriptorNumber = EMAC_NUM_TX_FRAG-1;
|
|
|
|
/* Tx Descriptors Point to 0 */
|
|
LPC_EMAC->TxProduceIndex = 0;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Set Station MAC address for EMAC module
|
|
* @param[in] abStationAddr Pointer to Station address that contains 6-bytes
|
|
* of MAC address.
|
|
* @return None
|
|
**********************************************************************/
|
|
void setEmacAddr(uint8_t abStationAddr[])
|
|
{
|
|
/* Set the Ethernet MAC Address registers */
|
|
LPC_EMAC->SA0 = ((uint32_t)abStationAddr[5] << 8) | (uint32_t)abStationAddr[4];
|
|
LPC_EMAC->SA1 = ((uint32_t)abStationAddr[3] << 8) | (uint32_t)abStationAddr[2];
|
|
LPC_EMAC->SA2 = ((uint32_t)abStationAddr[1] << 8) | (uint32_t)abStationAddr[0];
|
|
}
|
|
|
|
|
|
/************************** GLOBAL/PUBLIC FUNCTIONS *************************/
|
|
|
|
/** @defgroup EMAC_Public_Functions
|
|
* @{
|
|
*/
|
|
|
|
/*********************************************************************//**
|
|
* @brief Write data to PHY
|
|
* @param[in] PhyReg PHY register address
|
|
* @param[in] Value Register Value
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_Write_PHY (uint8_t PhyReg, uint16_t Value)
|
|
{
|
|
unsigned int tout;
|
|
|
|
LPC_EMAC->MADR = ((EMAC_Configs.bPhyAddr & 0x1F) << 8 )| (PhyReg & 0x1F);
|
|
LPC_EMAC->MWTD = Value;
|
|
|
|
/* Wait utill operation completed */
|
|
tout = 0;
|
|
|
|
for (tout = 0; tout < EMAC_MII_WR_TOUT; tout++)
|
|
{
|
|
if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Read data from PHY register
|
|
* @param[in] PhyReg PHY register address
|
|
* @return Register value
|
|
**********************************************************************/
|
|
uint16_t EMAC_Read_PHY (uint8_t PhyReg)
|
|
{
|
|
unsigned int tout;
|
|
|
|
LPC_EMAC->MADR = ((EMAC_Configs.bPhyAddr & 0x1F) << 8 )| (PhyReg & 0x1F);
|
|
LPC_EMAC->MCMD = EMAC_MCMD_READ;
|
|
|
|
/* Wait until operation completed */
|
|
tout = 0;
|
|
for (tout = 0; tout < EMAC_MII_RD_TOUT; tout++)
|
|
{
|
|
if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
LPC_EMAC->MCMD = 0;
|
|
return (LPC_EMAC->MRDD);
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Set Full/Half Duplex Mode
|
|
* @param[in] full_duplex 0: Half-duplex, 1: Full-duplex
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_SetFullDuplexMode(uint8_t full_duplex)
|
|
{
|
|
if(full_duplex)
|
|
{
|
|
LPC_EMAC->MAC2 |= EMAC_MAC2_FULL_DUP;
|
|
LPC_EMAC->Command |= EMAC_CR_FULL_DUP;
|
|
LPC_EMAC->IPGT = EMAC_IPGT_FULL_DUP;
|
|
}
|
|
else
|
|
{
|
|
LPC_EMAC->IPGT = EMAC_IPGT_HALF_DUP;
|
|
}
|
|
}
|
|
/*********************************************************************//**
|
|
* @brief Set PHY Speed
|
|
* @param[in] mode_100Mbps 0: 10Mbps, 1: 100Mbps
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_SetPHYSpeed(uint8_t mode_100Mbps)
|
|
{
|
|
if(mode_100Mbps)
|
|
{
|
|
LPC_EMAC->SUPP = EMAC_SUPP_SPEED;
|
|
}
|
|
else
|
|
{
|
|
LPC_EMAC->SUPP = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Initializes the EMAC peripheral according to the specified
|
|
* parameters in the EMAC_ConfigStruct.
|
|
* @param[in] EMAC_ConfigStruct Pointer to a EMAC_CFG_Type structure
|
|
* that contains the configuration information for the
|
|
* specified EMAC peripheral.
|
|
* @return None
|
|
*
|
|
* Note: This function will initialize EMAC module according to procedure below:
|
|
* - Remove the soft reset condition from the MAC
|
|
* - Configure the PHY via the MIIM interface of the MAC
|
|
* - Select RMII mode
|
|
* - Configure the transmit and receive DMA engines, including the descriptor arrays
|
|
* - Configure the host registers (MAC1,MAC2 etc.) in the MAC
|
|
* - Enable the receive and transmit data paths
|
|
* In default state after initializing, Rx Done and Tx Done interrupt are enabled,
|
|
* nad all interrupts are also enabled
|
|
* (Ref. from LPC17xx UM)
|
|
**********************************************************************/
|
|
int32_t EMAC_Init(EMAC_CFG_Type *EMAC_ConfigStruct)
|
|
{
|
|
/* Initialize the EMAC Ethernet controller. */
|
|
volatile int32_t tout, tmp;
|
|
|
|
EMAC_Configs = *EMAC_ConfigStruct;
|
|
|
|
/* Set up power for Ethernet module */
|
|
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, ENABLE);
|
|
|
|
/* Enable P1 Ethernet Pins. */
|
|
|
|
/* Reset all EMAC internal modules */
|
|
LPC_EMAC->MAC1 = EMAC_MAC1_RES_TX | EMAC_MAC1_RES_MCS_TX | EMAC_MAC1_RES_RX |
|
|
EMAC_MAC1_RES_MCS_RX | EMAC_MAC1_SIM_RES | EMAC_MAC1_SOFT_RES;
|
|
|
|
LPC_EMAC->Command = EMAC_CR_REG_RES | EMAC_CR_TX_RES | EMAC_CR_RX_RES | EMAC_CR_PASS_RUNT_FRM;
|
|
|
|
/* A short delay after reset. */
|
|
for (tout = 100; tout; tout--);
|
|
|
|
/* Initialize MAC control registers. */
|
|
LPC_EMAC->MAC1 = EMAC_MAC1_PASS_ALL;
|
|
LPC_EMAC->MAC2 = EMAC_MAC2_CRC_EN | EMAC_MAC2_PAD_EN;
|
|
LPC_EMAC->MAXF = EMAC_ETH_MAX_FLEN;
|
|
/*
|
|
* Find the clock that close to desired target clock
|
|
*/
|
|
tmp = CLKPWR_GetCLK(CLKPWR_CLKTYPE_CPU)/ EMAC_MCFG_MII_MAXCLK;
|
|
for (tout = 0; tout < sizeof (EMAC_clkdiv); tout++)
|
|
{
|
|
if (EMAC_clkdiv[tout] >= tmp)
|
|
break;
|
|
}
|
|
|
|
if(tout >= sizeof (EMAC_clkdiv))
|
|
return ERROR;
|
|
tout++;
|
|
|
|
// Set Frame size
|
|
LPC_EMAC->MAXF = EMAC_ConfigStruct->nMaxFrameSize;
|
|
|
|
// Write to MAC configuration register and reset
|
|
LPC_EMAC->MCFG = EMAC_MCFG_CLK_SEL(tout) | EMAC_MCFG_RES_MII;
|
|
|
|
// release reset
|
|
LPC_EMAC->MCFG &= ~(EMAC_MCFG_RES_MII);
|
|
LPC_EMAC->CLRT = EMAC_CLRT_DEF;
|
|
LPC_EMAC->IPGR = EMAC_IPGR_P2_DEF;
|
|
|
|
/* Enable Reduced MII interface. */
|
|
LPC_EMAC->Command = EMAC_CR_RMII | EMAC_CR_PASS_RUNT_FRM;
|
|
|
|
|
|
// Initilialize PHY
|
|
if(EMAC_ConfigStruct->pfnPHYInit != NULL)
|
|
{
|
|
if(EMAC_ConfigStruct->pfnPHYInit(&EMAC_ConfigStruct->PhyCfg) != SUCCESS)
|
|
{
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
// Set EMAC address
|
|
setEmacAddr(EMAC_ConfigStruct->pbEMAC_Addr);
|
|
|
|
/* Initialize Tx and Rx DMA Descriptors */
|
|
rx_descr_init ();
|
|
tx_descr_init ();
|
|
|
|
// Set Receive Filter register: enable broadcast and multicast
|
|
LPC_EMAC->RxFilterCtrl = EMAC_RFC_MCAST_EN | EMAC_RFC_BCAST_EN | EMAC_RFC_PERFECT_EN;
|
|
|
|
/* Enable Rx Done and Tx Done interrupt for EMAC */
|
|
EMAC_IntCmd((EMAC_INT_RX_OVERRUN | EMAC_INT_RX_ERR | EMAC_INT_RX_FIN \
|
|
| EMAC_INT_RX_DONE | EMAC_INT_TX_UNDERRUN | EMAC_INT_TX_ERR \
|
|
| EMAC_INT_TX_FIN | EMAC_INT_TX_DONE), ENABLE);
|
|
|
|
/* Reset all interrupts */
|
|
LPC_EMAC->IntClear = 0xFFFF;
|
|
|
|
/* Enable receive and transmit mode of MAC Ethernet core */
|
|
EMAC_TxEnable();
|
|
EMAC_RxEnable();
|
|
|
|
NVIC_EnableIRQ(ENET_IRQn);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief De-initializes the EMAC peripheral registers to their
|
|
* default reset values.
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_DeInit(void)
|
|
{
|
|
// Disable all interrupt
|
|
LPC_EMAC->IntEnable = 0x00;
|
|
|
|
// Clear all pending interrupt
|
|
LPC_EMAC->IntClear = (0xFF) | (EMAC_INT_SOFT_INT | EMAC_INT_WAKEUP);
|
|
|
|
LPC_EMAC->Command = 0;
|
|
|
|
/* TurnOff power for Ethernet module */
|
|
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, DISABLE);
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief EMAC TX API modules
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_TxEnable( void )
|
|
{
|
|
LPC_EMAC->Command |= EMAC_CR_TX_EN;
|
|
return;
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief EMAC RX API modules
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_TxDisable( void )
|
|
{
|
|
LPC_EMAC->Command &= ~EMAC_CR_TX_EN;
|
|
return;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief EMAC RX API modules
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_RxEnable( void )
|
|
{
|
|
LPC_EMAC->Command |= EMAC_CR_RX_EN;
|
|
LPC_EMAC->MAC1 |= EMAC_MAC1_REC_EN;
|
|
return;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief EMAC RX API modules
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_RxDisable( void )
|
|
{
|
|
LPC_EMAC->Command &= ~EMAC_CR_RX_EN;
|
|
LPC_EMAC->MAC1 &= ~EMAC_MAC1_REC_EN;
|
|
return;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get the status of given buffer.
|
|
* @param[in] idx Buffer index
|
|
* @return EMAC_BUFF_AVAILABLE/EMAC_BUFF_FULL/EMAC_BUFF_PARTIAL_FULL
|
|
*
|
|
**********************************************************************/
|
|
EMAC_BUFF_STATUS EMAC_GetBufferSts(EMAC_BUFF_IDX idx)
|
|
{
|
|
uint32_t consume_idx, produce_idx;
|
|
uint32_t max_frag_num;
|
|
|
|
// Get the consume index, produce index and the buffer size
|
|
if(idx == EMAC_TX_BUFF)
|
|
{
|
|
consume_idx = LPC_EMAC->TxConsumeIndex;
|
|
produce_idx = LPC_EMAC->TxProduceIndex;
|
|
max_frag_num = LPC_EMAC->TxDescriptorNumber + 1;
|
|
}
|
|
else
|
|
{
|
|
consume_idx = LPC_EMAC->RxConsumeIndex;
|
|
produce_idx = LPC_EMAC->RxProduceIndex;
|
|
max_frag_num = LPC_EMAC->RxDescriptorNumber + 1;
|
|
}
|
|
|
|
// empty
|
|
if(consume_idx == produce_idx)
|
|
return EMAC_BUFF_EMPTY;
|
|
|
|
// Full
|
|
if(consume_idx == 0 &&
|
|
produce_idx == max_frag_num - 1)
|
|
return EMAC_BUFF_FULL;
|
|
|
|
// Wrap-around
|
|
if(consume_idx == produce_idx + 1)
|
|
return EMAC_BUFF_FULL;
|
|
|
|
return EMAC_BUFF_PARTIAL_FULL;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Allocate a descriptor for sending frame and get the coressponding buffer address
|
|
* @param[in] FrameSize The size of frame you want to send
|
|
* @return Address of the TX_DESC_PACKET buffer
|
|
**********************************************************************/
|
|
uint32_t EMAC_AllocTxBuff(uint16_t nFrameSize, uint8_t bLastFrame)
|
|
{
|
|
uint32_t idx;
|
|
uint32_t dp;
|
|
volatile uint32_t i;
|
|
|
|
idx = LPC_EMAC->TxProduceIndex;
|
|
|
|
while(EMAC_GetBufferSts(EMAC_TX_BUFF) == EMAC_BUFF_FULL)
|
|
{
|
|
for(i = 0; i < 1000000; i++) ;
|
|
}
|
|
|
|
dp = TX_DESC_PACKET(idx);
|
|
|
|
if(bLastFrame)
|
|
TX_DESC_CTRL(idx) = ((nFrameSize-1) & EMAC_TCTRL_SIZE) | (EMAC_TCTRL_INT | EMAC_TCTRL_LAST);
|
|
else
|
|
TX_DESC_CTRL(idx) = ((nFrameSize-1) & EMAC_TCTRL_SIZE) | (EMAC_TCTRL_INT);
|
|
|
|
return dp;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Increase the TxProduceIndex (after writting to the Transmit buffer
|
|
* to enable the Transmit buffer) and wrap-around the index if
|
|
* it reaches the maximum Transmit Number
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_UpdateTxProduceIndex(void)
|
|
{
|
|
// Get current Tx produce index
|
|
uint32_t idx = LPC_EMAC->TxProduceIndex;
|
|
|
|
/* Start frame transmission */
|
|
if (++idx == LPC_EMAC->TxDescriptorNumber + 1) idx = 0;
|
|
LPC_EMAC->TxProduceIndex = idx;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get current status value of receive data (due to TxProduceIndex)
|
|
* @param[in] None
|
|
* @return Current value of receive data (due to TxProduceIndex)
|
|
**********************************************************************/
|
|
uint32_t EMAC_GetTxFrameStatus(void)
|
|
{
|
|
uint32_t idx;
|
|
|
|
idx = LPC_EMAC->TxProduceIndex;
|
|
return (TX_STAT_INFO(idx));
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Write data to Tx packet data buffer at current index due to
|
|
* TxProduceIndex
|
|
* @param[in] pDataStruct store the address and the size of buffer that saves data.
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_WritePacketBuffer(EMAC_PACKETBUF_Type *pDataStruct)
|
|
{
|
|
uint16_t* pDest;
|
|
uint16_t* pSource = (uint16_t*)pDataStruct->pbDataBuf;
|
|
uint32_t size = pDataStruct->ulDataLen;
|
|
int32_t frame_num;
|
|
uint32_t tmp;
|
|
uint32_t max_frame_size = LPC_EMAC->MAXF;
|
|
|
|
size = (size + 1) & 0xFFFE; // round Size up to next even number
|
|
frame_num = size/max_frame_size;
|
|
|
|
if(size == 0)
|
|
return;
|
|
|
|
while(frame_num >= 0)
|
|
{
|
|
tmp = (frame_num > 0)? max_frame_size:size;
|
|
|
|
if(tmp == 0)
|
|
break;
|
|
|
|
// Setup descriptors and data
|
|
if(frame_num == 0)
|
|
pDest = (uint16_t*)EMAC_AllocTxBuff(tmp, 1); // last frame
|
|
else
|
|
pDest = (uint16_t*)EMAC_AllocTxBuff(tmp, 0);
|
|
|
|
// Copy data
|
|
while (tmp > 0)
|
|
{
|
|
*pDest++ = *pSource++;
|
|
tmp -= 2;
|
|
}
|
|
frame_num--;
|
|
size -= tmp;
|
|
|
|
// Update produce index
|
|
EMAC_UpdateTxProduceIndex();
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get current status value of receive data (due to RxConsumeIndex)
|
|
* @param[in] None
|
|
* @return Current value of receive data (due to RxConsumeIndex)
|
|
**********************************************************************/
|
|
uint32_t EMAC_GetRxFrameStatus(void)
|
|
{
|
|
uint32_t idx;
|
|
|
|
idx = LPC_EMAC->RxConsumeIndex;
|
|
return (RX_STAT_INFO(idx));
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get size of current Received data in received buffer (due to
|
|
* RxConsumeIndex)
|
|
* @param[in] None
|
|
* @return Size of received data
|
|
**********************************************************************/
|
|
uint32_t EMAC_GetRxFrameSize(void)
|
|
{
|
|
uint32_t idx;
|
|
|
|
idx = LPC_EMAC->RxConsumeIndex;
|
|
|
|
return (((RX_STAT_INFO(idx)) & EMAC_RINFO_SIZE)+1);
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get the address of TX_DESC_PACKET buffer so that user can access from application
|
|
* @param[in] None
|
|
* @return Address of the TX_DESC_PACKET buffer
|
|
**********************************************************************/
|
|
|
|
uint32_t EMAC_GetRxBuffer(void)
|
|
{
|
|
uint32_t idx;
|
|
|
|
idx = LPC_EMAC->RxConsumeIndex;
|
|
|
|
return RX_DESC_PACKET(idx);
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Increase the RxConsumeIndex (after reading the Receive buffer
|
|
* to release the Receive buffer) and wrap-around the index if
|
|
* it reaches the maximum Receive Number
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_UpdateRxConsumeIndex(void)
|
|
{
|
|
// Get current Rx consume index
|
|
uint32_t idx = LPC_EMAC->RxConsumeIndex;
|
|
|
|
/* Release frame from EMAC buffer */
|
|
if (++idx == EMAC_NUM_RX_FRAG) idx = 0;
|
|
|
|
LPC_EMAC->RxConsumeIndex = idx;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Standard EMAC IRQ Handler. This sub-routine will check
|
|
* these following interrupt and call the call-back function
|
|
* if they're already installed:
|
|
* - Overrun Error interrupt in RX Queue
|
|
* - Receive Error interrupt: AlignmentError, RangeError,
|
|
* LengthError, SymbolError, CRCError or NoDescriptor or Overrun
|
|
* - RX Finished Process Descriptors interrupt (ProduceIndex == ConsumeIndex)
|
|
* - Receive Done interrupt: Read received frame to the internal buffer
|
|
* - Transmit Under-run interrupt
|
|
* - Transmit errors interrupt : LateCollision, ExcessiveCollision
|
|
* and ExcessiveDefer, NoDescriptor or Under-run
|
|
* - TX Finished Process Descriptors interrupt (ProduceIndex == ConsumeIndex)
|
|
* - Transmit Done interrupt
|
|
* - Interrupt triggered by software
|
|
* - Interrupt triggered by a Wakeup event detected by the receive filter
|
|
* @param[in] None
|
|
* @return None
|
|
**********************************************************************/
|
|
void ENET_IRQHandler(void)
|
|
{
|
|
/* EMAC Ethernet Controller Interrupt function. */
|
|
uint32_t int_stat;
|
|
int32_t RxLen;
|
|
|
|
// Get EMAC interrupt status
|
|
while ((int_stat = (LPC_EMAC->IntStatus & LPC_EMAC->IntEnable)) != 0)
|
|
{
|
|
// Clear interrupt status
|
|
LPC_EMAC->IntClear = int_stat;
|
|
|
|
if(int_stat & (EMAC_INT_RX_OVERRUN |EMAC_INT_RX_ERR ))
|
|
{
|
|
uint32_t ulFrameSts = EMAC_GetRxFrameStatus();
|
|
uint32_t ulErrCode = 0;
|
|
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_CRC_ERR) ? EMAC_CRC_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_SYM_ERR) ? EMAC_SYMBOL_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_LEN_ERR) ? EMAC_LENGTH_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_ALIGN_ERR) ? EMAC_ALIGN_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_OVERRUN) ? EMAC_OVERRUN_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_NO_DESCR) ? EMAC_RX_NO_DESC_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_RINFO_FAIL_FILT) ? EMAC_FILTER_FAILED_ERR:0;
|
|
|
|
if(ulErrCode == 0)
|
|
{
|
|
/* Note:
|
|
* The EMAC doesn't distinguish the frame type and frame length,
|
|
* so, e.g. when the IP(0x8000) or ARP(0x0806) packets are received,
|
|
* it compares the frame type with the max length and gives the
|
|
* "Range" error. In fact, this bit is not an error indication,
|
|
* but simply a statement by the chip regarding the status of
|
|
* the received frame
|
|
*/
|
|
int_stat &= ~EMAC_INT_RX_ERR;
|
|
}
|
|
else
|
|
{
|
|
if(EMAC_Configs.pfnErrorReceive != NULL)
|
|
EMAC_Configs.pfnErrorReceive(ulErrCode);
|
|
}
|
|
}
|
|
|
|
if(int_stat & (EMAC_INT_TX_UNDERRUN|EMAC_INT_TX_ERR ))
|
|
{
|
|
uint32_t ulFrameSts = EMAC_GetTxFrameStatus();
|
|
uint32_t ulErrCode = 0;
|
|
|
|
ulErrCode |= (ulFrameSts & EMAC_TINFO_EXCESS_DEF) ? EMAC_EXCESSIVE_DEFER_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_TINFO_EXCESS_COL) ? EMAC_EXCESSIVE_COLLISION_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_TINFO_LATE_COL) ? EMAC_LATE_COLLISION_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_TINFO_UNDERRUN) ? EMAC_UNDERRUN_ERR:0;
|
|
ulErrCode |= (ulFrameSts & EMAC_TINFO_NO_DESCR) ? EMAC_TX_NO_DESC_ERR:0;
|
|
if(EMAC_Configs.pfnErrorReceive != NULL)
|
|
EMAC_Configs.pfnErrorReceive(ulErrCode);
|
|
}
|
|
|
|
if(int_stat & EMAC_INT_RX_DONE)
|
|
{
|
|
|
|
/* Packet received. */
|
|
if(EMAC_GetBufferSts(EMAC_RX_BUFF) != EMAC_BUFF_EMPTY)
|
|
{
|
|
// Get data size
|
|
RxLen = EMAC_GetRxFrameSize();
|
|
if(RxLen > 0)
|
|
{
|
|
// trip out 4-bytes CRC field, note that length in (-1) style format
|
|
RxLen -= 3;
|
|
|
|
if((EMAC_GetRxFrameStatus() & EMAC_RINFO_ERR_MASK )== 0)
|
|
{
|
|
uint16_t *pDest = (uint16_t*) &saFrameBuffers[sbCurrFrameID][sulCurrFrameSz];
|
|
uint16_t *pSource = (uint16_t*)EMAC_GetRxBuffer();
|
|
sulCurrFrameSz += RxLen;
|
|
|
|
if(sulCurrFrameSz >= EMAC_MAX_FRAME_SIZE)
|
|
{
|
|
sulCurrFrameSz = 0;
|
|
if(EMAC_Configs.pfnErrorReceive != NULL)
|
|
EMAC_Configs.pfnErrorReceive(EMAC_LENGTH_ERR);
|
|
}
|
|
else
|
|
{
|
|
// Copy data
|
|
while (RxLen > 0)
|
|
{
|
|
*pDest++ = *pSource++;
|
|
RxLen -= 2;
|
|
}
|
|
if(EMAC_GetRxFrameStatus() & EMAC_RINFO_LAST_FLAG)
|
|
{
|
|
|
|
if(EMAC_Configs.pfnFrameReceive != NULL)
|
|
{
|
|
EMAC_Configs.pfnFrameReceive((uint16_t*)saFrameBuffers[sbCurrFrameID], sulCurrFrameSz);
|
|
}
|
|
sulCurrFrameSz = 0;
|
|
sbCurrFrameID ++;
|
|
if(sbCurrFrameID >= EMAC_MAX_FRAME_NUM)
|
|
sbCurrFrameID = 0;
|
|
}
|
|
/* Release frame from EMAC buffer */
|
|
EMAC_UpdateRxConsumeIndex();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if(int_stat & EMAC_INT_TX_FIN && (EMAC_Configs.pfnTransmitFinish != NULL))
|
|
{
|
|
EMAC_Configs.pfnTransmitFinish();
|
|
}
|
|
|
|
if(int_stat & EMAC_INT_SOFT_INT && (EMAC_Configs.pfnSoftInt!= NULL))
|
|
{
|
|
EMAC_Configs.pfnSoftInt();
|
|
}
|
|
|
|
if(int_stat & EMAC_INT_WAKEUP && (EMAC_Configs.pfnWakeup!= NULL))
|
|
{
|
|
EMAC_Configs.pfnWakeup();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Enable/Disable hash filter functionality for specified destination
|
|
* MAC address in EMAC module
|
|
* @param[in] dstMAC_addr Pointer to the first MAC destination address, should
|
|
* be 6-bytes length, in order LSB to the MSB
|
|
* @param[in] NewState New State of this command, should be:
|
|
* - ENABLE.
|
|
* - DISABLE.
|
|
* @return None
|
|
*
|
|
* Note:
|
|
* The standard Ethernet cyclic redundancy check (CRC) function is calculated from
|
|
* the 6 byte destination address in the Ethernet frame (this CRC is calculated
|
|
* anyway as part of calculating the CRC of the whole frame), then bits [28:23] out of
|
|
* the 32 bits CRC result are taken to form the hash. The 6 bit hash is used to access
|
|
* the hash table: it is used as an index in the 64 bit HashFilter register that has been
|
|
* programmed with accept values. If the selected accept value is 1, the frame is
|
|
* accepted.
|
|
**********************************************************************/
|
|
void EMAC_SetHashFilter(uint8_t dstMAC_addr[], FunctionalState NewState)
|
|
{
|
|
uint32_t *pReg;
|
|
uint32_t tmp;
|
|
int32_t crc;
|
|
|
|
// Calculate the CRC from the destination MAC address
|
|
crc = EMAC_CRCCalc(dstMAC_addr, 6);
|
|
// Extract the value from CRC to get index value for hash filter table
|
|
crc = (crc >> 23) & 0x3F;
|
|
|
|
pReg = (crc > 31) ? ((uint32_t *)&LPC_EMAC->HashFilterH) \
|
|
: ((uint32_t *)&LPC_EMAC->HashFilterL);
|
|
tmp = (crc > 31) ? (crc - 32) : crc;
|
|
if (NewState == ENABLE)
|
|
{
|
|
(*pReg) |= (1UL << tmp);
|
|
}
|
|
else
|
|
{
|
|
(*pReg) &= ~(1UL << tmp);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Calculates CRC code for number of bytes in the frame
|
|
* @param[in] frame_no_fcs Pointer to the first byte of the frame
|
|
* @param[in] frame_len length of the frame without the FCS
|
|
* @return the CRC as a 32 bit integer
|
|
**********************************************************************/
|
|
int32_t EMAC_CRCCalc(uint8_t frame_no_fcs[], int32_t frame_len)
|
|
{
|
|
int i; // iterator
|
|
int j; // another iterator
|
|
char byte; // current byte
|
|
int crc; // CRC result
|
|
int q0, q1, q2, q3; // temporary variables
|
|
|
|
crc = 0xFFFFFFFF;
|
|
|
|
for (i = 0; i < frame_len; i++)
|
|
{
|
|
byte = *frame_no_fcs++;
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
if (((crc >> 28) ^ (byte >> 3)) & 0x00000001)
|
|
{
|
|
q3 = 0x04C11DB7;
|
|
}
|
|
else
|
|
{
|
|
q3 = 0x00000000;
|
|
}
|
|
|
|
if (((crc >> 29) ^ (byte >> 2)) & 0x00000001)
|
|
{
|
|
q2 = 0x09823B6E;
|
|
}
|
|
else
|
|
{
|
|
q2 = 0x00000000;
|
|
}
|
|
|
|
if (((crc >> 30) ^ (byte >> 1)) & 0x00000001)
|
|
{
|
|
q1 = 0x130476DC;
|
|
}
|
|
else
|
|
{
|
|
q1 = 0x00000000;
|
|
}
|
|
|
|
if (((crc >> 31) ^ (byte >> 0)) & 0x00000001)
|
|
{
|
|
q0 = 0x2608EDB8;
|
|
}
|
|
else
|
|
{
|
|
q0 = 0x00000000;
|
|
}
|
|
|
|
crc = (crc << 4) ^ q3 ^ q2 ^ q1 ^ q0;
|
|
|
|
byte >>= 4;
|
|
}
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Enable/Disable Filter mode for each specified type EMAC peripheral
|
|
* @param[in] ulFilterMode Filter mode, should be:
|
|
* - EMAC_RFC_UCAST_EN: all frames of unicast types
|
|
* will be accepted
|
|
* - EMAC_RFC_BCAST_EN: broadcast frame will be
|
|
* accepted
|
|
* - EMAC_RFC_MCAST_EN: all frames of multicast
|
|
* types will be accepted
|
|
* - EMAC_RFC_UCAST_HASH_EN: The imperfect hash
|
|
* filter will be applied to unicast addresses
|
|
* - EMAC_RFC_MCAST_HASH_EN: The imperfect hash
|
|
* filter will be applied to multicast addresses
|
|
* - EMAC_RFC_PERFECT_EN: the destination address
|
|
* will be compared with the 6 byte station address
|
|
* programmed in the station address by the filter
|
|
* - EMAC_RFC_MAGP_WOL_EN: the result of the magic
|
|
* packet filter will generate a WoL interrupt when
|
|
* there is a match
|
|
* - EMAC_RFC_PFILT_WOL_EN: the result of the perfect address
|
|
* matching filter and the imperfect hash filter will
|
|
* generate a WoL interrupt when there is a match
|
|
* @param[in] NewState New State of this command, should be:
|
|
* - ENABLE
|
|
* - DISABLE
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_SetFilterMode(uint32_t ulFilterMode, FunctionalState NewState)
|
|
{
|
|
if (NewState == ENABLE)
|
|
{
|
|
LPC_EMAC->RxFilterCtrl |= ulFilterMode;
|
|
|
|
if((ulFilterMode & EMAC_RFC_MCAST_HASH_EN) &&
|
|
((ulFilterMode & EMAC_RFC_MCAST_EN) == 0))
|
|
{
|
|
LPC_EMAC->RxFilterCtrl &= ~EMAC_RFC_MCAST_EN;
|
|
}
|
|
if((ulFilterMode & EMAC_RFC_UCAST_HASH_EN) &&
|
|
((ulFilterMode & EMAC_RFC_UCAST_EN) == 0))
|
|
{
|
|
LPC_EMAC->RxFilterCtrl &= ~EMAC_RFC_UCAST_EN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPC_EMAC->RxFilterCtrl &= ~ulFilterMode;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get status of Wake On LAN Filter for each specified
|
|
* type in EMAC peripheral, clear this status if it is set
|
|
* @param[in] ulFilterMode WoL Filter mode, should be:
|
|
* - EMAC_WOL_UCAST: unicast frames caused WoL
|
|
* - EMAC_WOL_UCAST: broadcast frame caused WoL
|
|
* - EMAC_WOL_MCAST: multicast frame caused WoL
|
|
* - EMAC_WOL_UCAST_HASH: unicast frame that passes the
|
|
* imperfect hash filter caused WoL
|
|
* - EMAC_WOL_MCAST_HASH: multicast frame that passes the
|
|
* imperfect hash filter caused WoL
|
|
* - EMAC_WOL_PERFECT:perfect address matching filter
|
|
* caused WoL
|
|
* - EMAC_WOL_RX_FILTER: the receive filter caused WoL
|
|
* - EMAC_WOL_MAG_PACKET: the magic packet filter caused WoL
|
|
* @return SET/RESET
|
|
**********************************************************************/
|
|
FlagStatus EMAC_GetWoLStatus(uint32_t ulWoLMode)
|
|
{
|
|
if (LPC_EMAC->RxFilterWoLStatus & ulWoLMode)
|
|
{
|
|
LPC_EMAC->RxFilterWoLClear = ulWoLMode;
|
|
|
|
return SET;
|
|
}
|
|
else
|
|
{
|
|
return RESET;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Enable/Disable interrupt for each type in EMAC
|
|
* @param[in] ulIntType Interrupt Type, should be:
|
|
* - EMAC_INT_RX_OVERRUN: Receive Overrun
|
|
* - EMAC_INT_RX_ERR: Receive Error
|
|
* - EMAC_INT_RX_FIN: Receive Descriptor Finish
|
|
* - EMAC_INT_RX_DONE: Receive Done
|
|
* - EMAC_INT_TX_UNDERRUN: Transmit Under-run
|
|
* - EMAC_INT_TX_ERR: Transmit Error
|
|
* - EMAC_INT_TX_FIN: Transmit descriptor finish
|
|
* - EMAC_INT_TX_DONE: Transmit Done
|
|
* - EMAC_INT_SOFT_INT: Software interrupt
|
|
* - EMAC_INT_WAKEUP: Wakeup interrupt
|
|
* @param[in] NewState New State of this function, should be:
|
|
* - ENABLE.
|
|
* - DISABLE.
|
|
* @return None
|
|
**********************************************************************/
|
|
void EMAC_IntCmd(uint32_t ulIntType, FunctionalState NewState)
|
|
{
|
|
if (NewState == ENABLE)
|
|
{
|
|
LPC_EMAC->IntEnable |= ulIntType;
|
|
}
|
|
else
|
|
{
|
|
LPC_EMAC->IntEnable &= ~(ulIntType);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Check whether if specified interrupt flag is set or not
|
|
* for each interrupt type in EMAC and clear interrupt pending
|
|
* if it is set.
|
|
* @param[in] ulIntType Interrupt Type, should be:
|
|
* - EMAC_INT_RX_OVERRUN: Receive Overrun
|
|
* - EMAC_INT_RX_ERR: Receive Error
|
|
* - EMAC_INT_RX_FIN: Receive Descriptor Finish
|
|
* - EMAC_INT_RX_DONE: Receive Done
|
|
* - EMAC_INT_TX_UNDERRUN: Transmit Under-run
|
|
* - EMAC_INT_TX_ERR: Transmit Error
|
|
* - EMAC_INT_TX_FIN: Transmit descriptor finish
|
|
* - EMAC_INT_TX_DONE: Transmit Done
|
|
* - EMAC_INT_SOFT_INT: Software interrupt
|
|
* - EMAC_INT_WAKEUP: Wakeup interrupt
|
|
* @return New state of specified interrupt (SET or RESET)
|
|
**********************************************************************/
|
|
IntStatus EMAC_IntGetStatus(uint32_t ulIntType)
|
|
{
|
|
if (LPC_EMAC->IntStatus & ulIntType)
|
|
{
|
|
LPC_EMAC->IntClear = ulIntType;
|
|
return SET;
|
|
}
|
|
else
|
|
{
|
|
return RESET;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
#endif /*_EMAC*/
|