mbed library sources. Supersedes mbed-src.

Fork of mbed-dev by mbed official

Revision:
149:156823d33999
Parent:
144:ef7eb2e8f9f7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_NUVOTON/TARGET_NUC472/device/StdDriver/nuc472_emac.c	Fri Oct 28 11:17:30 2016 +0100
@@ -0,0 +1,874 @@
+/**************************************************************************//**
+ * @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. ***/