Newer
Older
mbed-os / hal / targets / cmsis / TARGET_NUVOTON / TARGET_NUC472 / StdDriver / nuc472_emac.c
@ccli8 ccli8 on 27 Jul 2016 28 KB Support NUMAKER_PFM_NUC472
/**************************************************************************//**
 * @file     emac.c
 * @version  V1.00
 * $Revision: 14 $
 * $Date: 14/05/29 1:13p $
 * @brief    NUC472/NUC442 EMAC driver source file
 *
 * @note
 * Copyright (C) 2013 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "NUC472_442.h"

/** @addtogroup NUC472_442_Device_Driver NUC472/NUC442 Device Driver
  @{
*/

/** @addtogroup NUC472_442_EMAC_Driver EMAC Driver
  @{
*/


// Below are structure, definitions, static variables used locally by EMAC driver and does not want to parse by doxygen unless HIDDEN_SYMBOLS is defined
/// @cond HIDDEN_SYMBOLS

/** @addtogroup NUC472_442_EMAC_EXPORTED_CONSTANTS EMAC Exported Constants
  @{
*/
// Un-comment to print EMAC debug message
//#define EMAC_DBG
#ifndef EMAC_DBG
#define printf(...)
#endif

// PHY Register Description
#define PHY_CNTL_REG    0x00        ///< PHY control register address
#define PHY_STATUS_REG  0x01        ///< PHY status register address
#define PHY_ID1_REG     0x02        ///< PHY ID1 register
#define PHY_ID2_REG     0x03        ///< PHY ID2 register
#define PHY_ANA_REG     0x04        ///< PHY auto-negotiation advertisement register
#define PHY_ANLPA_REG   0x05        ///< PHY auto-negotiation link partner availability register
#define PHY_ANE_REG     0x06        ///< PHY auto-negotiation expansion register

//PHY Control Register
#define PHY_CNTL_RESET_PHY      (1 << 15)
#define PHY_CNTL_DR_100MB       (1 << 13)
#define PHY_CNTL_ENABLE_AN      (1 << 12)
#define PHY_CNTL_POWER_DOWN     (1 << 11)
#define PHY_CNTL_RESTART_AN     (1 << 9)
#define PHY_CNTL_FULLDUPLEX     (1 << 8)

// PHY Status Register
#define PHY_STATUS_AN_COMPLETE   (1 << 5)
#define PHY_STATUS_LINK_VALID    (1 << 3)

// PHY Auto-negotiation Advertisement Register
#define PHY_ANA_DR100_TX_FULL   (1 << 8)
#define PHY_ANA_DR100_TX_HALF   (1 << 7)
#define PHY_ANA_DR10_TX_FULL    (1 << 6)
#define PHY_ANA_DR10_TX_HALF    (1 << 5)
#define PHY_ANA_IEEE_802_3_CSMA_CD   (1 << 0)

// PHY Auto-negotiation Link Partner Advertisement Register
#define PHY_ANLPA_DR100_TX_FULL   (1 << 8)
#define PHY_ANLPA_DR100_TX_HALF   (1 << 7)
#define PHY_ANLPA_DR10_TX_FULL    (1 << 6)
#define PHY_ANLPA_DR10_TX_HALF    (1 << 5)

// EMAC Tx/Rx descriptor's owner bit
#define EMAC_DESC_OWN_EMAC 0x80000000  ///< Set owner to EMAC
#define EMAC_DESC_OWN_CPU  0x00000000  ///< Set owner to CPU

// Rx Frame Descriptor Status
#define EMAC_RXFD_RTSAS   0x0080  ///< Time Stamp Available
#define EMAC_RXFD_RP      0x0040  ///< Runt Packet
#define EMAC_RXFD_ALIE    0x0020  ///< Alignment Error
#define EMAC_RXFD_RXGD    0x0010  ///< Receiving Good packet received
#define EMAC_RXFD_PTLE    0x0008  ///< Packet Too Long Error
#define EMAC_RXFD_CRCE    0x0002  ///< CRC Error
#define EMAC_RXFD_RXINTR  0x0001  ///< Interrupt on receive

// Tx Frame Descriptor's Control bits
#define EMAC_TXFD_TTSEN     0x08      ///< Tx time stamp enable
#define EMAC_TXFD_INTEN     0x04      ///< Tx interrupt enable
#define EMAC_TXFD_CRCAPP    0x02      ///< Append CRC
#define EMAC_TXFD_PADEN     0x01      ///< Padding mode enable

// Tx Frame Descriptor Status
#define EMAC_TXFD_TXINTR 0x0001  ///< Interrupt on Transmit
#define EMAC_TXFD_DEF    0x0002  ///< Transmit deferred 
#define EMAC_TXFD_TXCP   0x0008  ///< Transmission Completion 
#define EMAC_TXFD_EXDEF  0x0010  ///< Exceed Deferral
#define EMAC_TXFD_NCS    0x0020  ///< No Carrier Sense Error
#define EMAC_TXFD_TXABT  0x0040  ///< Transmission Abort 
#define EMAC_TXFD_LC     0x0080  ///< Late Collision 
#define EMAC_TXFD_TXHA   0x0100  ///< Transmission halted
#define EMAC_TXFD_PAU    0x0200  ///< Paused
#define EMAC_TXFD_SQE    0x0400  ///< SQE error 
#define EMAC_TXFD_TTSAS  0x0800  ///< Time Stamp available

/*@}*/ /* end of group NUC472_442_EMAC_EXPORTED_CONSTANTS */

/** @addtogroup NUC472_442_EMAC_EXPORTED_TYPEDEF EMAC Exported Type Defines
  @{
*/

/** Tx/Rx buffer descriptor structure */
typedef struct {
    uint32_t u32Status1;   ///< Status word 1
    uint32_t u32Data;      ///< Pointer to data buffer
    uint32_t u32Status2;   ///< Status word 2
    uint32_t u32Next;      ///< Pointer to next descriptor
    uint32_t u32Backup1;   ///< For backup descriptor fields over written by time stamp
    uint32_t u32Backup2;   ///< For backup descriptor fields over written by time stamp
} EMAC_DESCRIPTOR_T;

/** Tx/Rx buffer structure */
typedef struct {
    uint8_t au8Buf[1520];
} EMAC_FRAME_T;

/*@}*/ /* end of group NUC472_442_EMAC_EXPORTED_TYPEDEF */

// local variables
static volatile EMAC_DESCRIPTOR_T rx_desc[EMAC_RX_DESC_SIZE];
static volatile EMAC_FRAME_T rx_buf[EMAC_RX_DESC_SIZE];
static volatile EMAC_DESCRIPTOR_T tx_desc[EMAC_TX_DESC_SIZE];
static volatile EMAC_FRAME_T tx_buf[EMAC_TX_DESC_SIZE];


static uint32_t u32CurrentTxDesc, u32NextTxDesc, u32CurrentRxDesc;
static uint32_t s_u32EnableTs = 0;

/** @addtogroup NUC472_442_EMAC_EXPORTED_FUNCTIONS EMAC Exported Functions
  @{
*/

/**
  * @brief  Trigger EMAC Rx function
  * @param  None
  * @return None
  */
#define EMAC_TRIGGER_RX() do{EMAC->RXST = 0;}while(0)

/**
  * @brief  Trigger EMAC Tx function
  * @param  None
  * @return None
  */
#define EMAC_TRIGGER_TX() do{EMAC->TXST = 0;}while(0)

/**
  * @brief  Write PHY register
  * @param[in]  u32Reg PHY register number
  * @param[in]  u32Addr PHY address, this address is board dependent
  * @param[in] u32Data data to write to PHY register
  * @return None
  */
static void EMAC_MdioWrite(uint32_t u32Reg, uint32_t u32Addr, uint32_t u32Data)
{
    // Set data register
    EMAC->MIIMDAT = u32Data ;
    // Set PHY address, PHY register address, busy bit and write bit
    EMAC->MIIMCTL = u32Reg | (u32Addr << 8) | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk;
    // Wait write complete by polling busy bit.
    while(EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);

}

/**
  * @brief  Read PHY register
  * @param[in]  u32Reg PHY register number
  * @param[in]  u32Addr PHY address, this address is board dependent
  * @return Value read from PHY register
  */
static uint32_t EMAC_MdioRead(uint32_t u32Reg, uint32_t u32Addr)
{
    // Set PHY address, PHY register address, busy bit
    EMAC->MIIMCTL = u32Reg | (u32Addr << EMAC_MIIMCTL_PHYADDR_Pos) | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk;
    // Wait read complete by polling busy bit
    while(EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);
    // Get return data
    return EMAC->MIIMDAT;
}


/**
  * @brief  Initialize PHY chip, check for the auto-negotiation result.
  * @param  None
  * @return None
  */
static void EMAC_PhyInit(void)
{
    uint32_t reg;

    // Reset Phy Chip
    EMAC_MdioWrite(PHY_CNTL_REG, EMAC_PHY_ADDR, PHY_CNTL_RESET_PHY);

    // Wait until reset complete
    while (1) {
        reg = EMAC_MdioRead(PHY_CNTL_REG, EMAC_PHY_ADDR) ;
        if ((reg & PHY_CNTL_RESET_PHY)==0)
            break;
    }

    if(!EMAC_MdioRead(PHY_STATUS_REG, EMAC_PHY_ADDR) & PHY_STATUS_LINK_VALID) {     // Cable not connected
        printf("Unplug\n..");
        EMAC->CTL &= ~EMAC_CTL_OPMODE_Msk;
        EMAC->CTL &= ~EMAC_CTL_FUDUP_Msk;
        return;
    }
    // Configure auto negotiation capability
    EMAC_MdioWrite(PHY_ANA_REG, EMAC_PHY_ADDR, PHY_ANA_DR100_TX_FULL |
                   PHY_ANA_DR100_TX_HALF |
                   PHY_ANA_DR10_TX_FULL |
                   PHY_ANA_DR10_TX_HALF |
                   PHY_ANA_IEEE_802_3_CSMA_CD);
    // Restart auto negotiation
    EMAC_MdioWrite(PHY_CNTL_REG, EMAC_PHY_ADDR, EMAC_MdioRead(PHY_CNTL_REG, EMAC_PHY_ADDR) | PHY_CNTL_RESTART_AN);

    // Wait for auto-negotiation complete
    while(!(EMAC_MdioRead(PHY_STATUS_REG, EMAC_PHY_ADDR) & PHY_STATUS_AN_COMPLETE));

    // Check link valid again. Some PHYs needs to check result after link valid bit set
    while(!(EMAC_MdioRead(PHY_STATUS_REG, EMAC_PHY_ADDR) & PHY_STATUS_LINK_VALID));

    // Check link partner capability
    reg = EMAC_MdioRead(PHY_ANLPA_REG, EMAC_PHY_ADDR) ;
    if (reg & PHY_ANLPA_DR100_TX_FULL) {
        printf("100F\n");
        EMAC->CTL |= EMAC_CTL_OPMODE_Msk;
        EMAC->CTL |= EMAC_CTL_FUDUP_Msk;
    } else if (reg & PHY_ANLPA_DR100_TX_HALF) {
        printf("100H\n");
        EMAC->CTL |= EMAC_CTL_OPMODE_Msk;
        EMAC->CTL &= ~EMAC_CTL_FUDUP_Msk;
    } else if (reg & PHY_ANLPA_DR10_TX_FULL) {
        printf("10F\n");
        EMAC->CTL &= ~EMAC_CTL_OPMODE_Msk;
        EMAC->CTL |= EMAC_CTL_FUDUP_Msk;
    } else {
        printf("10H\n");
        EMAC->CTL &= ~EMAC_CTL_OPMODE_Msk;
        EMAC->CTL &= ~EMAC_CTL_FUDUP_Msk;
    }
}

/**
  * @brief  Initial EMAC Tx descriptors and get Tx descriptor base address
  * @param None
  * @return None
  */
static void EMAC_TxDescInit(void)
{
    uint32_t i;

    // Get Frame descriptor's base address.
    EMAC->TXDSA = (uint32_t)&tx_desc[0];
    u32NextTxDesc = u32CurrentTxDesc = (uint32_t)&tx_desc[0];

    for(i = 0; i < EMAC_TX_DESC_SIZE; i++) {

        if(s_u32EnableTs)
            tx_desc[i].u32Status1 = EMAC_TXFD_PADEN | EMAC_TXFD_CRCAPP | EMAC_TXFD_INTEN;
        else
            tx_desc[i].u32Status1 = EMAC_TXFD_PADEN | EMAC_TXFD_CRCAPP | EMAC_TXFD_INTEN | EMAC_TXFD_TTSEN;

        tx_desc[i].u32Data = (uint32_t)((uint32_t)&tx_buf[i]);
        tx_desc[i].u32Backup1 = tx_desc[i].u32Data;
        tx_desc[i].u32Status2 = 0;
        tx_desc[i].u32Next = (uint32_t)&tx_desc[(i + 1) % EMAC_TX_DESC_SIZE];
        tx_desc[i].u32Backup2 = tx_desc[i].u32Next;

    }

}


/**
  * @brief  Initial EMAC Rx descriptors and get Rx descriptor base address
  * @param None
  * @return None
  */
static void EMAC_RxDescInit(void)
{

    uint32_t i;

    // Get Frame descriptor's base address.
    EMAC->RXDSA = (uint32_t)&rx_desc[0];
    u32CurrentRxDesc = (uint32_t)&rx_desc[0];

    for(i=0; i < EMAC_RX_DESC_SIZE; i++) {
        rx_desc[i].u32Status1 = EMAC_DESC_OWN_EMAC;
        rx_desc[i].u32Data = (uint32_t)((uint32_t)&rx_buf[i]);
        rx_desc[i].u32Backup1 = rx_desc[i].u32Data;
        rx_desc[i].u32Status2 = 0;
        rx_desc[i].u32Next = (uint32_t)&rx_desc[(i + 1) % EMAC_RX_DESC_SIZE];
        rx_desc[i].u32Backup2 = rx_desc[i].u32Next;
    }

}

/**
  * @brief  Convert subsecond value to nano second
  * @param[in]  subsec Subsecond value to be convert
  * @return Nano second
  */
static uint32_t EMAC_Subsec2Nsec(uint32_t subsec)
{
    // 2^31 subsec == 10^9 ns
    uint64_t i;
    i = 1000000000ll * subsec;
    i >>= 31;
    return(i);
}

/**
  * @brief  Convert nano second to subsecond value
  * @param[in]  nsec Nano second to be convert
  * @return Subsecond
  */
static uint32_t EMAC_Nsec2Subsec(uint32_t nsec)
{
    // 10^9 ns =  2^31 subsec
    uint64_t i;
    i = (1ll << 31) * nsec;
    i /= 1000000000;
    return(i);
}


/*@}*/ /* end of group NUC472_442_EMAC_EXPORTED_FUNCTIONS */



/// @endcond HIDDEN_SYMBOLS


/** @addtogroup NUC472_442_EMAC_EXPORTED_FUNCTIONS EMAC Exported Functions
  @{
*/


// Basic configuration functions
/**
  * @brief  Initialize EMAC interface, including descriptors, MAC address, and PHY.
  * @param[in]  pu8MacAddr  Pointer to uint8_t array holds MAC address
  * @return None
  * @note This API sets EMAC to work in RMII mode, but could configure to MII mode later with \ref EMAC_ENABLE_MII_INTF macro
  * @note This API configures EMAC to receive all broadcast and multicast packets, but could configure to other settings with
  *       \ref EMAC_ENABLE_RECV_BCASTPKT, \ref EMAC_DISABLE_RECV_BCASTPKT, \ref EMAC_ENABLE_RECV_MCASTPKT, and \ref EMAC_DISABLE_RECV_MCASTPKT
  * @note Receive(RX) and transmit(TX) are not enabled yet, application must call \ref EMAC_ENABLE_RX and \ref EMAC_ENABLE_TX to
  *       enable receive and transmit function.
  */
void EMAC_Open(uint8_t *pu8MacAddr)
{
    // Enable transmit and receive descriptor
    EMAC_TxDescInit();
    EMAC_RxDescInit();

    // Set the CAM Control register and the MAC address value
    EMAC_SetMacAddr(pu8MacAddr);

    // Configure the MAC interrupt enable register.
    EMAC->INTEN = EMAC_INTEN_RXIEN_Msk |
                  EMAC_INTEN_TXIEN_Msk |
                  EMAC_INTEN_RXGDIEN_Msk |
                  EMAC_INTEN_TXCPIEN_Msk |
                  EMAC_INTEN_RXBEIEN_Msk |
                  EMAC_INTEN_TXBEIEN_Msk |
                  EMAC_INTEN_RDUIEN_Msk |
                  EMAC_INTEN_TSALMIEN_Msk |
                  EMAC_INTEN_WOLIEN_Msk;

    // Configure the MAC control register.
    EMAC->CTL = EMAC_CTL_STRIPCRC_Msk |
                EMAC_CTL_RMIIEN_Msk |
                EMAC_CTL_RMIIRXCTL_Msk;

    //Accept packets for us and all broadcast and multicast packets
    EMAC->CAMCTL =  EMAC_CAMCTL_CMPEN_Msk |
                    EMAC_CAMCTL_AMP_Msk |
                    EMAC_CAMCTL_ABP_Msk;

    EMAC_PhyInit();
}

/**
  * @brief  This function stop all receive and transmit activity and disable MAC interface
  * @param None
  * @return None
  */

void EMAC_Close(void)
{
    EMAC->CTL |= EMAC_CTL_RST_Msk;
}

/**
  * @brief  Set the device MAC address
  * @param[in]  pu8MacAddr  Pointer to uint8_t array holds MAC address
  * @return None
  */
void EMAC_SetMacAddr(uint8_t *pu8MacAddr)
{
    EMAC_EnableCamEntry(0, pu8MacAddr);

}

/**
  * @brief Fill a CAM entry for MAC address comparison.
  * @param[in] u32Entry MAC entry to fill. Entry 0 is used to store device MAC address, do not overwrite the setting in it.
  * @param[in] pu8MacAddr  Pointer to uint8_t array holds MAC address
  * @return None
  */
void EMAC_EnableCamEntry(uint32_t u32Entry, uint8_t *pu8MacAddr)
{
    uint32_t u32Lsw, u32Msw;

    u32Lsw = (pu8MacAddr[4] << 24) |
             (pu8MacAddr[5] << 16);
    u32Msw = (pu8MacAddr[0] << 24)|
             (pu8MacAddr[1] << 16)|
             (pu8MacAddr[2] << 8)|
             pu8MacAddr[3];

    *(uint32_t volatile *)(&EMAC->CAM0M + u32Entry * 4) = u32Msw;
    *(uint32_t volatile *)(&EMAC->CAM0L + u32Entry * 4) = u32Lsw;

    EMAC->CAMEN |= (1 << u32Entry);
}

/**
  * @brief  Disable a specified CAM entry
  * @param[in]  u32Entry CAM entry to be disabled
  * @return None
  */
void EMAC_DisableCamEntry(uint32_t u32Entry)
{
    EMAC->CAMEN &= ~(1 << u32Entry);
}

// Receive functions
/**
  * @brief Receive an Ethernet packet
  * @param[in] pu8Data Pointer to a buffer to store received packet (4 byte CRC removed)
  * @param[in] pu32Size Received packet size (without 4 byte CRC).
  * @return Packet receive success or not
  * @retval 0 No packet available for receive
  * @retval 1 A packet is received
  * @note Return 0 doesn't guarantee the packet will be sent and received successfully.
  */
uint32_t EMAC_RecvPkt(uint8_t *pu8Data, uint32_t *pu32Size)
{
    EMAC_DESCRIPTOR_T *desc;
    uint32_t status, reg;
    uint32_t u32Count = 0;

    // Clear Rx interrupt flags
    reg = EMAC->INTSTS;
    EMAC->INTSTS = reg & 0xFFFF;  // Clear all RX related interrupt status

    if (reg & EMAC_INTSTS_RXBEIF_Msk) {
        // Bus error occurred, this is usually a bad sign about software bug and will occur again...
        printf("RX bus error\n");
    } else {

        // Get Rx Frame Descriptor
        desc = (EMAC_DESCRIPTOR_T *)u32CurrentRxDesc;

        // If we reach last recv Rx descriptor, leave the loop
        if(EMAC->CRXDSA == (uint32_t)desc)
            return(0);
        if ((desc->u32Status1 | EMAC_DESC_OWN_EMAC) != EMAC_DESC_OWN_EMAC) { // ownership=CPU

            status = desc->u32Status1 >> 16;

            // If Rx frame is good, process received frame
            if(status & EMAC_RXFD_RXGD) {
                // lower 16 bit in descriptor status1 stores the Rx packet length
                *pu32Size = desc->u32Status1 & 0xffff;
                memcpy(pu8Data, (uint8_t *)desc->u32Backup1, *pu32Size);
                u32Count = 1;
            } else {
                // Save Error status if necessary
                if (status & EMAC_RXFD_RP);
                if (status & EMAC_RXFD_ALIE);
                if (status & EMAC_RXFD_PTLE);
                if (status & EMAC_RXFD_CRCE);
            }
        }
    }
    return(u32Count);
}

/**
  * @brief Receive an Ethernet packet and the time stamp while it's received
  * @param[out] pu8Data Pointer to a buffer to store received packet (4 byte CRC removed)
  * @param[out] pu32Size Received packet size (without 4 byte CRC).
  * @param[out] pu32Sec Second value while packet sent
  * @param[out] pu32Nsec Nano second value while packet sent
  * @return Packet receive success or not
  * @retval 0 No packet available for receive
  * @retval 1 A packet is received
  * @note Return 0 doesn't guarantee the packet will be sent and received successfully.
  * @note Largest Ethernet packet is 1514 bytes after stripped CRC, application must give
  *       a buffer large enough to store such packet
  */
uint32_t EMAC_RecvPktTS(uint8_t *pu8Data, uint32_t *pu32Size, uint32_t *pu32Sec, uint32_t *pu32Nsec)
{
    EMAC_DESCRIPTOR_T *desc;
    uint32_t status, reg;
    uint32_t u32Count = 0;

    // Clear Rx interrupt flags
    reg = EMAC->INTSTS;
    EMAC->INTSTS = reg & 0xFFFF; // Clear all Rx related interrupt status

    if (reg & EMAC_INTSTS_RXBEIF_Msk) {
        // Bus error occurred, this is usually a bad sign about software bug and will occur again...
        printf("RX bus error\n");
    } else {

        // Get Rx Frame Descriptor
        desc = (EMAC_DESCRIPTOR_T *)u32CurrentRxDesc;

        // If we reach last recv Rx descriptor, leave the loop
        if(EMAC->CRXDSA == (uint32_t)desc)
            return(0);
        if ((desc->u32Status1 | EMAC_DESC_OWN_EMAC) != EMAC_DESC_OWN_EMAC) { // ownership=CPU

            status = desc->u32Status1 >> 16;

            // If Rx frame is good, process received frame
            if(status & EMAC_RXFD_RXGD) {
                // lower 16 bit in descriptor status1 stores the Rx packet length
                *pu32Size = desc->u32Status1 & 0xffff;
                memcpy(pu8Data, (uint8_t *)desc->u32Backup1, *pu32Size);

                *pu32Sec = desc->u32Next; // second stores in descriptor's NEXT field
                *pu32Nsec = EMAC_Subsec2Nsec(desc->u32Data); // Sub nano second store in DATA field

                u32Count = 1;
            } else {
                // Save Error status if necessary
                if (status & EMAC_RXFD_RP);
                if (status & EMAC_RXFD_ALIE);
                if (status & EMAC_RXFD_PTLE);
                if (status & EMAC_RXFD_CRCE);
            }
        }
    }
    return(u32Count);
}

/**
  * @brief Clean up process after a packet is received
  * @param None
  * @return None
  * @details EMAC Rx interrupt service routine \b must call this API to release the resource use by receive process
  * @note Application can only call this function once every time \ref EMAC_RecvPkt or \ref EMAC_RecvPktTS returns 1
  */
void EMAC_RecvPktDone(void)
{
    EMAC_DESCRIPTOR_T *desc;
    // Get Rx Frame Descriptor
    desc = (EMAC_DESCRIPTOR_T *)u32CurrentRxDesc;

    // restore descriptor link list and data pointer they will be overwrite if time stamp enabled
    desc->u32Data = desc->u32Backup1;
    desc->u32Next = desc->u32Backup2;

    // Change ownership to DMA for next use
    desc->u32Status1 |= EMAC_DESC_OWN_EMAC;

    // Get Next Frame Descriptor pointer to process
    desc = (EMAC_DESCRIPTOR_T *)desc->u32Next;

    // Save last processed Rx descriptor
    u32CurrentRxDesc = (uint32_t)desc;

    EMAC_TRIGGER_RX();
}

// Transmit functions

/**
  * @brief Send an Ethernet packet
  * @param[in] pu8Data Pointer to a buffer holds the packet to transmit
  * @param[in] u32Size Packet size (without 4 byte CRC).
  * @return Packet transmit success or not
  * @retval 0 Transmit failed due to descriptor unavailable.
  * @retval 1 Packet is copied to descriptor and triggered to transmit.
  * @note Return 1 doesn't guarantee the packet will be sent and received successfully.
  */
uint32_t EMAC_SendPkt(uint8_t *pu8Data, uint32_t u32Size)
{
    EMAC_DESCRIPTOR_T *desc;
    uint32_t status;

    // Get Tx frame descriptor & data pointer
    desc = (EMAC_DESCRIPTOR_T *)u32NextTxDesc;

    status = desc->u32Status1;

    // Check descriptor ownership
    if((status & EMAC_DESC_OWN_EMAC))
        return(0);

    memcpy((uint8_t *)desc->u32Data, pu8Data, u32Size);

    // Set Tx descriptor transmit byte count
    desc->u32Status2 = u32Size;

    // Change descriptor ownership to EMAC
    desc->u32Status1 |= EMAC_DESC_OWN_EMAC;

    // Get next Tx descriptor
    u32NextTxDesc = (uint32_t)(desc->u32Next);

    // Trigger EMAC to send the packet
    EMAC_TRIGGER_TX();

    return(1);
}


/**
  * @brief Clean up process after packet(s) are sent
  * @param None
  * @return Number of packet sent between two function calls
  * @details EMAC Tx interrupt service routine \b must call this API or \ref EMAC_SendPktDoneTS to
  *          release the resource use by transmit process
  */
uint32_t EMAC_SendPktDone(void)
{
    EMAC_DESCRIPTOR_T *desc;
    uint32_t status, reg;
    uint32_t last_tx_desc;
    uint32_t u32Count = 0;

    reg = EMAC->INTSTS;
    // Clear Tx interrupt flags
    EMAC->INTSTS = reg & (0xFFFF0000 & ~EMAC_INTSTS_TSALMIF_Msk);


    if (reg & EMAC_INTSTS_TXBEIF_Msk) {
        // Bus error occurred, this is usually a bad sign about software bug and will occur again...
        printf("TX bus error\n");
    } else {
        // Process the descriptor(s).
        last_tx_desc = EMAC->CTXDSA ;
        // Get our first descriptor to process
        desc = (EMAC_DESCRIPTOR_T *) u32CurrentTxDesc;
        do {
            // Descriptor ownership is still EMAC, so this packet haven't been send.
            if(desc->u32Status1 & EMAC_DESC_OWN_EMAC)
                break;
            // Get Tx status stored in descriptor
            status = desc->u32Status2 >> 16;
            if (status & EMAC_TXFD_TXCP) {
                u32Count++;
            } else {
                // Do nothing here on error.
                if (status & EMAC_TXFD_TXABT);
                if (status & EMAC_TXFD_DEF);
                if (status & EMAC_TXFD_PAU);
                if (status & EMAC_TXFD_EXDEF);
                if (status & EMAC_TXFD_NCS);
                if (status & EMAC_TXFD_SQE);
                if (status & EMAC_TXFD_LC);
                if (status & EMAC_TXFD_TXHA);
            }

            // restore descriptor link list and data pointer they will be overwrite if time stamp enabled
            desc->u32Data = desc->u32Backup1;
            desc->u32Next = desc->u32Backup2;
            // go to next descriptor in link
            desc = (EMAC_DESCRIPTOR_T *)desc->u32Next;
        } while (last_tx_desc != (uint32_t)desc);    // If we reach last sent Tx descriptor, leave the loop
        // Save last processed Tx descriptor
        u32CurrentTxDesc = (uint32_t)desc;
    }
    return(u32Count);
}

/**
  * @brief Clean up process after a packet is sent, and get the time stamp while packet is sent
  * @param[in]  pu32Sec Second value while packet sent
  * @param[in]  pu32Nsec Nano second value while packet sent
  * @return If a packet sent successfully
  * @retval 0 No packet sent successfully, and the value in *pu32Sec and *pu32Nsec are meaningless
  * @retval 1 A packet sent successfully, and the value in *pu32Sec and *pu32Nsec is the time stamp while packet sent
  * @details EMAC Tx interrupt service routine \b must call this API or \ref EMAC_SendPktDone to
  *          release the resource use by transmit process
  */
uint32_t EMAC_SendPktDoneTS(uint32_t *pu32Sec, uint32_t *pu32Nsec)
{

    EMAC_DESCRIPTOR_T *desc;
    uint32_t status, reg;
    uint32_t u32Count = 0;

    reg = EMAC->INTSTS;
    // Clear Tx interrupt flags
    EMAC->INTSTS = reg & (0xFFFF0000 & ~EMAC_INTSTS_TSALMIF_Msk);


    if (reg & EMAC_INTSTS_TXBEIF_Msk) {
        // Bus error occurred, this is usually a bad sign about software bug and will occur again...
        printf("TX bus error\n");
    } else {
        // Process the descriptor.
        // Get our first descriptor to process
        desc = (EMAC_DESCRIPTOR_T *) u32CurrentTxDesc;

        // Descriptor ownership is still EMAC, so this packet haven't been send.
        if(desc->u32Status1 & EMAC_DESC_OWN_EMAC)
            return(0);
        // Get Tx status stored in descriptor
        status = desc->u32Status2 >> 16;
        if (status & EMAC_TXFD_TXCP) {
            u32Count = 1;
            *pu32Sec = desc->u32Next; // second stores in descriptor's NEXT field
            *pu32Nsec = EMAC_Subsec2Nsec(desc->u32Data); // Sub nano second store in DATA field
        } else {
            // Do nothing here on error.
            if (status & EMAC_TXFD_TXABT);
            if (status & EMAC_TXFD_DEF);
            if (status & EMAC_TXFD_PAU);
            if (status & EMAC_TXFD_EXDEF);
            if (status & EMAC_TXFD_NCS);
            if (status & EMAC_TXFD_SQE);
            if (status & EMAC_TXFD_LC);
            if (status & EMAC_TXFD_TXHA);
        }

        // restore descriptor link list and data pointer they will be overwrite if time stamp enabled
        desc->u32Data = desc->u32Backup1;
        desc->u32Next = desc->u32Backup2;
        // go to next descriptor in link
        desc = (EMAC_DESCRIPTOR_T *)desc->u32Next;

        // Save last processed Tx descriptor
        u32CurrentTxDesc = (uint32_t)desc;
    }

    return(u32Count);
}

// IEEE 1588 functions
/**
  * @brief  Enable IEEE1588 time stamp function and set current time
  * @param[in]  u32Sec Second value
  * @param[in]  u32Nsec Nano second value
  * @return None
  */
void EMAC_EnableTS(uint32_t u32Sec, uint32_t u32Nsec)
{
    double f;
    uint32_t reg;
    EMAC->TSCTL = EMAC_TSCTL_TSEN_Msk;
    EMAC->UPDSEC = u32Sec;   // Assume current time is 0 sec + 0 nano sec
    EMAC->UPDSUBSEC = EMAC_Nsec2Subsec(u32Nsec);

    // PTP source clock is 84MHz (Real chip using PLL). Each tick is 11.90ns
    // Assume we want to set each tick to 100ns.
    // Increase register = (100 * 2^31) / (10^9) = 214.71 =~ 215 = 0xD7
    // Addend register = 2^32 * tick_freq / (84MHz), where tick_freq = (2^31 / 215) MHz
    // From above equation, addend register = 2^63 / (84M * 215) ~= 510707200 = 0x1E70C600
    // So:
    //  EMAC->TSIR = 0xD7;
    //  EMAC->TSAR = 0x1E70C600;
    f = (100.0 * 2147483648.0) / (1000000000.0) + 0.5;
    EMAC->TSINC = (reg = (uint32_t)f);
    f = (double)9223372036854775808.0 / ((double)(CLK_GetHCLKFreq()) * (double)reg);
    EMAC->TSADDEND = (uint32_t)f;
    EMAC->TSCTL |= (EMAC_TSCTL_TSUPDATE_Msk | EMAC_TSCTL_TSIEN_Msk | EMAC_TSCTL_TSMODE_Msk); // Fine update
}

/**
  * @brief  Disable IEEE1588 time stamp function
  * @param None
  * @return None
  */
void EMAC_DisableTS(void)
{
    EMAC->TSCTL = 0;
}

/**
  * @brief  Get current time stamp
  * @param[out]  pu32Sec Current second value
  * @param[out]  pu32Nsec Current nano second value
  * @return None
  */
void EMAC_GetTime(uint32_t *pu32Sec, uint32_t *pu32Nsec)
{
    // Must read TSLSR firstly. Hardware will preserve TSMSR value at the time TSLSR read.
    *pu32Nsec = EMAC_Subsec2Nsec(EMAC->TSSUBSEC);
    *pu32Sec = EMAC->TSSEC;
}

/**
  * @brief  Set current time stamp
  * @param[in]  u32Sec Second value
  * @param[in]  u32Nsec Nano second value
  * @return None
  */
void EMAC_SetTime(uint32_t u32Sec, uint32_t u32Nsec)
{
    // Disable time stamp counter before update time value (clear EMAC_TSCTL_TSIEN_Msk)
    EMAC->TSCTL = EMAC_TSCTL_TSEN_Msk;
    EMAC->UPDSEC = u32Sec;
    EMAC->UPDSUBSEC = EMAC_Nsec2Subsec(u32Nsec);
    EMAC->TSCTL |= (EMAC_TSCTL_TSIEN_Msk | EMAC_TSCTL_TSMODE_Msk);

}

/**
  * @brief  Enable alarm function and set alarm time
  * @param[in]  u32Sec Second value to trigger alarm
  * @param[in]  u32Nsec Nano second value to trigger alarm
  * @return None
  */
void EMAC_EnableAlarm(uint32_t u32Sec, uint32_t u32Nsec)
{

    EMAC->ALMSEC = u32Sec;
    EMAC->ALMSUBSEC = EMAC_Nsec2Subsec(u32Nsec);
    EMAC->TSCTL |= EMAC_TSCTL_TSALMEN_Msk;

}

/**
  * @brief  Disable alarm function
  * @param  None
  * @return None
  */
void EMAC_DisableAlarm(void)
{

    EMAC->TSCTL &= ~EMAC_TSCTL_TSALMEN_Msk;

}

/**
  * @brief  Add a offset to current time
  * @param[in]  u32Neg Offset is negative value (u32Neg == 1) or positive value (u32Neg == 0).
  * @param[in]  u32Sec Second value to add to current time
  * @param[in]  u32Nsec Nano second value to add to current time
  * @return None
  */
void EMAC_UpdateTime(uint32_t u32Neg, uint32_t u32Sec, uint32_t u32Nsec)
{
    EMAC->UPDSEC = u32Sec;
    EMAC->UPDSUBSEC = EMAC_Nsec2Subsec(u32Nsec);
    if(u32Neg)
        EMAC->UPDSUBSEC |= BIT31;   // Set bit 31 indicates this is a negative value

    EMAC->TSCTL |= EMAC_TSCTL_TSUPDATE_Msk;

}


/*@}*/ /* end of group NUC472_442_EMAC_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group NUC472_442_EMAC_Driver */

/*@}*/ /* end of group NUC472_442_Device_Driver */

/*** (C) COPYRIGHT 2013 Nuvoton Technology Corp. ***/