📄 emac.c
字号:
/*****************************************************************************
* emac.c: Ethernet module file for NXP LPC230x Family Microprocessors
*
* Copyright(C) 2006, NXP Semiconductor
* All rights reserved.
*
* History
* 2006.09.01 ver 1.00 Prelimnary version, first Release
*
******************************************************************************/
#include <nxp/iolpc2378.h>
#include "type.h"
#include "irq.h"
#include "emac.h"
#include <intrinsics.h>
volatile DWORD Duplex;
volatile DWORD Speed;
volatile DWORD RXOverrunCount = 0;
volatile DWORD RXErrorCount = 0;
volatile DWORD TXUnderrunCount = 0;
volatile DWORD TXErrorCount = 0;
volatile DWORD RxFinishedCount = 0;
volatile DWORD TxFinishedCount = 0;
volatile DWORD TxDoneCount = 0;
volatile DWORD RxDoneCount = 0;
volatile DWORD CurrentRxPtr = EMAC_RX_BUFFER_ADDR;
volatile DWORD ReceiveLength = 0;
volatile DWORD PacketReceived = FALSE;
#if ENABLE_WOL
volatile DWORD WOLCount = 0;
volatile DWORD WOLArrived = FALSE;
#endif
static DWORD PhyAddr;
/******************************************************************************
** Function name: EMAC_TxEnable/EMAC_TxDisable
**
** Descriptions: EMAC TX API modules
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void EMAC_TxEnable( void )
{
COMMAND |= 0x02;
return;
}
void EMAC_TxDisable( void )
{
COMMAND &= ~0x02;
return;
}
/******************************************************************************
** Function name: EMAC_RxEnable/EMAC_RxDisable
**
** Descriptions: EMAC RX API modules
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void EMAC_RxEnable( void )
{
COMMAND |= 0x01;
MAC1 |= 0x01;
return;
}
void EMAC_RxDisable( void )
{
COMMAND &= ~0x01;
MAC1 &= ~0x01;
return;
}
/******************************************************************************
** Function name: EMACHandler
**
** Descriptions: EMAC interrupt handler
**
** parameters: None
** Returned value: None
**
******************************************************************************/
__irq __nested __arm void EMACHandler (void)
{
volatile DWORD IntStatus;
DWORD *rxptr;
__enable_interrupt(); /* handles nested interrupt */
IntStatus = INTSTATUS;
if ( IntStatus != 0 ) /* At least one interrupt */
{
#if ENABLE_WOL
if ( IntStatus & EMAC_INT_WOL )
{
INTCLEAR = EMAC_INT_WOL;
WOLCount++;
WOLArrived = TRUE;
/* the packet will be lost, no need to anything else, bail out */
VICAddress = 0; /* Acknowledge Interrupt */
return;
}
#endif
if ( IntStatus & EMAC_INT_RXOVERRUN )
{
INTCLEAR = EMAC_INT_RXOVERRUN;
RXOverrunCount++;
VICADDRESS = 0; /* Acknowledge Interrupt */
return;
}
if ( IntStatus & EMAC_INT_RXERROR )
{
INTCLEAR = EMAC_INT_RXERROR;
RXErrorCount++;
VICADDRESS = 0; /* Acknowledge Interrupt */
return;
}
if ( IntStatus & EMAC_INT_RXFINISHED )
{
INTCLEAR = EMAC_INT_RXFINISHED;
RxFinishedCount++;
/* Below should never happen or RX is seriously wrong */
while ( RXPRODUCEINDEX != (RXCONSUMEINDEX - 1) );
}
if ( IntStatus & EMAC_INT_RXDONE )
{
INTCLEAR = EMAC_INT_RXDONE;
ReceiveLength = EMACReceive( rxptr );
PacketReceived = TRUE;
RxDoneCount++;
}
if ( IntStatus & EMAC_INT_TXUNDERRUN )
{
INTCLEAR = EMAC_INT_TXUNDERRUN;
TXUnderrunCount++;
VICADDRESS = 0; /* Acknowledge Interrupt */
return;
}
if ( IntStatus & EMAC_INT_TXERROR )
{
INTCLEAR = EMAC_INT_TXERROR;
TXErrorCount++;
VICADDRESS = 0; /* Acknowledge Interrupt */
return;
}
if ( IntStatus & EMAC_INT_TXFINISHED )
{
INTCLEAR = EMAC_INT_TXFINISHED;
TxFinishedCount++;
}
if ( IntStatus & EMAC_INT_TXDONE )
{
INTCLEAR = EMAC_INT_TXDONE;
TxDoneCount++;
}
}
VICADDRESS = 0; /* Acknowledge Interrupt */
return;
}
/*****************************************************************************
** Function name: WritePHY
**
** Descriptions: Write Data to the PHY port
**
** parameters: PHY register, write data
** Returned value: None
**
*****************************************************************************/
void WritePHY( DWORD PHYReg, DWORD PHYData )
{
MADR = PhyAddr | PHYReg; /* [12:8] == PHY addr, [4:0]=0x00(BMCR) register addr */
MCMD = 0x0000; /* write command */
MWTD = PHYData;
while ( MIND_bit.BUSY );
return;
}
/*****************************************************************************
** Function name: ReadPHY
**
** Descriptions: Read data from the PHY port
**
** parameters: PHY register
** Returned value: PHY data
**
*****************************************************************************/
DWORD ReadPHY( DWORD PHYReg )
{
MADR = PhyAddr | PHYReg; /* [12:8] == PHY addr, [4:0]=0x00(BMCR) register addr */
MCMD = 0x0001; /* read command */
while ( MIND_bit.BUSY || MIND_bit.NOT_VALID );
MCMD = 0x0000;
return( MRDD );
}
/*****************************************************************************
** Function name: PHYInit
**
** Descriptions: initialize PHY port
**
** parameters: None
** Returned value: None
**
*****************************************************************************/
DWORD PHYInit( void )
{
DWORD i, regValue;
DWORD timeout;
#if RMII
/* RMII configuration */
MCFG = 0x0018; /* host clock divided by 20, no suppress preamble, no scan increment */
COMMAND |= 0x0200;
SUPP = 0x8900; /* RMII setting, Reset MII Management hardware,
PHY support: [8]=0 ->10 Mbps mode, =1 -> 100 Mbps mode */
/* (note bit 4 was set in original test, although spec says its unused) */
for ( i = 0; i < 0x20; i++ ); /* short delay */
SUPP = 0x0100;
#else
COMMAND &= ~0x0200;
SUPP = 0x0000;
#endif
/* Find PHY Address, because controller have pull-up and PHY have pull-down resistors
on RXD lines some times PHY latch different addresses - 0x01, 0x19, 0x09, 0x17 */
for(PhyAddr = (1UL << 8); PhyAddr < (32UL << 8); PhyAddr += (1UL << 8))
{
/* check PHY IDs to make sure the reset takes place and PHY
is in its default state. See Micrel PHY KS8721 Users Manual
for more details */
regValue = ReadPHY( PHY_PHYIDR1 );
if ( (regValue & 0xFFFF) != 0x0022 )
{
continue;
}
regValue = ReadPHY( PHY_PHYIDR2 );
if ( (regValue & 0xFFFF) == 0x1619 )
{
break;
}
}
if(PhyAddr == (32UL << 8))
{
// Can't find Micrel PHY
return( FALSE );
}
WritePHY( PHY_BMCR, BMCR_RESET );
for ( i = 0; i < 0x20; i++ ); /* short delay */
timeout = MII_BMSR_TIMEOUT;
while ( timeout != 0 )
{
regValue = ReadPHY( PHY_BMCR );
if ( (regValue & BMCR_RESET) == 0x0000 )
{
break; /* Reset self cleaned once the reset process is complete */
}
timeout--;
}
if ( timeout == 0 )
{
return ( FALSE );
}
if(ReadPHY( PHY_BMSR ) & BMSR_PREAMBLE_SUPP)
{
// PHY support preamble suppression
MCFG |= 1 << 1;
}
#if AUTO_NEGOTIATION_ENA != 0
/* set Auto-Negotiation Advertisement Register
a valid 802.3 selector field, 100 HD, 100 FD, 10 HD, 10 FD */
WritePHY( PHY_ANAR, 0x01E1 );
WritePHY( PHY_BMCR, BMCR_AN | BMCR_RE_AN ); /* auto negotiation, restart AN */
timeout = MII_BMSR_TIMEOUT;
while ( timeout != 0 )
{
regValue = ReadPHY( PHY_BMSR );
if ( (regValue & (BMSR_LINK_ESTABLISHED | BMSR_AUTO_DONE)) == (BMSR_LINK_ESTABLISHED | BMSR_AUTO_DONE))
{
break; /* link established if bit 0 is set */
}
timeout--;
}
if ( timeout == 0 )
{
return ( FALSE );
}
/* Link established from here on */
regValue = ReadPHY( PHY_PHYCR );
switch(regValue & PHYCR_MODE)
{
case 0x04: // 10 BASE T Half-duplex
Speed = SPEED_10;
Duplex = HALF_DUPLEX;
break;
case 0x08: // 100 BASE TX Half-duplex
Speed = SPEED_100;
Duplex = HALF_DUPLEX;
break;
case 0x14: // 10 BASE T Full-duplex
Speed = SPEED_10;
Duplex = FULL_DUPLEX;
break;
case 0x18: // 100 BASE TX Full-duplex
Speed = SPEED_100;
Duplex = FULL_DUPLEX;
break;
default:
return ( FALSE );
}
#else
/* Set fixed Speed and Duplex settings */
WritePHY( PHY_BMCR, ((FIX_SPEED == SPEED_100)?BMCR_SPEED_100:0) |
((FIX_DUPLEX == FULL_DUPLEX)?BMCR_DUPLEX:0));
timeout = MII_BMSR_TIMEOUT;
while ( timeout != 0 )
{
regValue = ReadPHY( PHY_BMSR );
if (regValue & BMSR_LINK_ESTABLISHED)
{
break; /* link established if bit 0 is set */
}
timeout--;
}
if ( timeout == 0 )
{
return ( FALSE );
}
Speed = FIX_SPEED;
Duplex = FIX_DUPLEX;
#endif // AUTO_NEGOTIATION_ENA != 0
return ( TRUE );
}
/*****************************************************************************
** Function name: EMACTxDesciptorInit
**
** Descriptions: initialize EMAC TX descriptor table
**
** parameters: None
** Returned value: None
**
*****************************************************************************/
void EMACTxDescriptorInit( void )
{
DWORD i;
DWORD *tx_desc_addr, *tx_status_addr;
/*-----------------------------------------------------------------------------
* setup the Tx status,descriptor registers --
* Note, the actual tx packet data is loaded into the ahb2_sram16k memory as part
* of the simulation
*----------------------------------------------------------------------------*/
TXDESCRIPTOR = TX_DESCRIPTOR_ADDR; /* Base addr of tx descriptor array */
TXSTATUS = TX_STATUS_ADDR; /* Base addr of tx status */
TXDESCRIPTORNUMBER = EMAC_TX_DESCRIPTOR_COUNT - 1; /* number of tx descriptors, 16 */
for ( i = 0; i < EMAC_TX_DESCRIPTOR_COUNT; i++ )
{
tx_desc_addr = (DWORD *)(TX_DESCRIPTOR_ADDR + i * 8); /* two words at a time, packet and control */
*tx_desc_addr = (DWORD)(EMAC_TX_BUFFER_ADDR + i * EMAC_BLOCK_SIZE);
*(tx_desc_addr+1) = (DWORD)(EMAC_TX_DESC_INT | (EMAC_BLOCK_SIZE - 1)); /* set size only */
}
for ( i = 0; i < EMAC_TX_DESCRIPTOR_COUNT; i++ )
{
tx_status_addr = (DWORD *)(TX_STATUS_ADDR + i * 4); /* TX status, one word only, status info. */
*tx_status_addr = (DWORD)0; /* initially, set status info to 0 */
}
TXPRODUCEINDEX = 0x0; /* TX descriptors point to zero */
return;
}
/*****************************************************************************
** Function name: EMACRxDesciptorInit
**
** Descriptions: initialize EMAC RX descriptor table
**
** parameters: None
** Returned value: None
**
*****************************************************************************/
void EMACRxDescriptorInit( void )
{
DWORD i;
DWORD *rx_desc_addr, *rx_status_addr;
/*-----------------------------------------------------------------------------
* setup the Rx status,descriptor registers --
* Note, the actual rx packet data is loaded into the ahb2_sram16k memory as part
* of the simulation
*----------------------------------------------------------------------------*/
RXDESCRIPTOR = RX_DESCRIPTOR_ADDR; /* Base addr of rx descriptor array */
RXSTATUS = RX_STATUS_ADDR; /* Base addr of rx status */
RXDESCRIPTORNUMBER = EMAC_RX_DESCRIPTOR_COUNT - 1; /* number of rx descriptors, 16 */
for ( i = 0; i < EMAC_RX_DESCRIPTOR_COUNT; i++ )
{
/* two words at a time, packet and control */
rx_desc_addr = (DWORD *)(RX_DESCRIPTOR_ADDR + i * 8);
*rx_desc_addr = (DWORD)(EMAC_RX_BUFFER_ADDR + i * EMAC_BLOCK_SIZE);
*(rx_desc_addr+1) = (DWORD)(EMAC_RX_DESC_INT | (EMAC_BLOCK_SIZE - 1)); /* set size only */
}
for ( i = 0; i < EMAC_RX_DESCRIPTOR_COUNT; i++ )
{
/* RX status, two words, status info. and status hash CRC. */
rx_status_addr = (DWORD *)(RX_STATUS_ADDR + i * 8);
*rx_status_addr = (DWORD)0; /* initially, set both status info and hash CRC to 0 */
*(rx_status_addr+1) = (DWORD)0;
}
RXCONSUMEINDEX = 0x0; /* RX descriptor points to zero */
return;
}
/*****************************************************************************
** Function name: EMACInit
**
** Descriptions: initialize EMAC port
**
** parameters: None
** Returned value: None
**
*****************************************************************************/
DWORD EMACInit( void )
{
DWORD regVal;
DWORD i;
/* turn on the ethernet MAC clock in PCONP, bit 30 */
regVal = PCONP;
regVal |= PCONP_EMAC_CLOCK;
PCONP = regVal;
/*------------------------------------------------------------------------------
* write to PINSEL2/3 to select the PHY functions on P1[17:0]
*-----------------------------------------------------------------------------*/
/* documentation needs to be updated */
#if RMII
// Pins assignment
//------------------------------------------------------
// Write to PINSEL2/3 to select the PHY functions on P1[17:0]
// P1.6, ENET-TX_CLK, has to be set for Rev '-' devices and it
// must not be set for Rev 'A
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -