/**************************************************************************//**
 * @file     emac.c
 * @version  V1.00
 * @brief    M480 EMAC driver source file
 *
 * @copyright (C) 2016 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "M480.h"


/** @addtogroup M480_Device_Driver M480 Device Driver
  @{
*/

/** @addtogroup M480_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 M480_EMAC_EXPORTED_CONSTANTS EMAC Exported Constants
  @{
*/

/* PHY Register Description */
#define PHY_CNTL_REG    0x00UL        /*!<  PHY control register address */
#define PHY_STATUS_REG  0x01UL        /*!<  PHY status register address */
#define PHY_ID1_REG     0x02UL        /*!<  PHY ID1 register */
#define PHY_ID2_REG     0x03UL        /*!<  PHY ID2 register */
#define PHY_ANA_REG     0x04UL        /*!<  PHY auto-negotiation advertisement register */
#define PHY_ANLPA_REG   0x05UL        /*!<  PHY auto-negotiation link partner availability register */
#define PHY_ANE_REG     0x06UL        /*!<  PHY auto-negotiation expansion register */

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

/* PHY Status Register */
#define PHY_STATUS_AN_COMPLETE   (1UL << 5UL)
#define PHY_STATUS_LINK_VALID    (1UL << 2UL)

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

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

/* EMAC Tx/Rx descriptor's owner bit */
#define EMAC_DESC_OWN_EMAC 0x80000000UL  /*!<  Set owner to EMAC */
#define EMAC_DESC_OWN_CPU  0x00000000UL  /*!<  Set owner to CPU */

/* Rx Frame Descriptor Status */
#define EMAC_RXFD_RTSAS   0x0080UL  /*!<  Time Stamp Available */
#define EMAC_RXFD_RP      0x0040UL  /*!<  Runt Packet */
#define EMAC_RXFD_ALIE    0x0020UL  /*!<  Alignment Error */
#define EMAC_RXFD_RXGD    0x0010UL  /*!<  Receiving Good packet received */
#define EMAC_RXFD_PTLE    0x0008UL  /*!<  Packet Too Long Error */
#define EMAC_RXFD_CRCE    0x0002UL  /*!<  CRC Error */
#define EMAC_RXFD_RXINTR  0x0001UL  /*!<  Interrupt on receive */

/* Tx Frame Descriptor's Control bits */
#define EMAC_TXFD_TTSEN     0x08UL      /*!<  Tx time stamp enable */
#define EMAC_TXFD_INTEN     0x04UL      /*!<  Tx interrupt enable */
#define EMAC_TXFD_CRCAPP    0x02UL      /*!<  Append CRC */
#define EMAC_TXFD_PADEN     0x01UL      /*!<  Padding mode enable */

/* Tx Frame Descriptor Status */
#define EMAC_TXFD_TXINTR 0x0001UL  /*!<  Interrupt on Transmit */
#define EMAC_TXFD_DEF    0x0002UL  /*!<  Transmit deferred  */
#define EMAC_TXFD_TXCP   0x0008UL  /*!<  Transmission Completion  */
#define EMAC_TXFD_EXDEF  0x0010UL  /*!<  Exceed Deferral */
#define EMAC_TXFD_NCS    0x0020UL  /*!<  No Carrier Sense Error */
#define EMAC_TXFD_TXABT  0x0040UL  /*!<  Transmission Abort  */
#define EMAC_TXFD_LC     0x0080UL  /*!<  Late Collision  */
#define EMAC_TXFD_TXHA   0x0100UL  /*!<  Transmission halted */
#define EMAC_TXFD_PAU    0x0200UL  /*!<  Paused */
#define EMAC_TXFD_SQE    0x0400UL  /*!<  SQE error  */
#define EMAC_TXFD_TTSAS  0x0800UL  /*!<  Time Stamp available */

/*@}*/ /* end of group M480_EMAC_EXPORTED_CONSTANTS */

/** @addtogroup M480_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 M480_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 = 0UL;

static void EMAC_MdioWrite(uint32_t u32Reg, uint32_t u32Addr, uint32_t u32Data);
static uint32_t EMAC_MdioRead(uint32_t u32Reg, uint32_t u32Addr);
static void EMAC_PhyInit(void);
static void EMAC_TxDescInit(void);
static void EMAC_RxDescInit(void);
static uint32_t EMAC_Subsec2Nsec(uint32_t subsec);
static uint32_t EMAC_Nsec2Subsec(uint32_t nsec);

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

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

/**
  * @brief  Trigger EMAC Tx function
  * @param  None
  * @return None
  */
#define EMAC_TRIGGER_TX() do{EMAC->TXST = 0UL;}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;
    uint32_t i = 0UL;

    /* 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)==0UL) {
            break;
        }
    }
    while(!(EMAC_MdioRead(PHY_STATUS_REG, EMAC_PHY_ADDR) & PHY_STATUS_LINK_VALID)) {
        if(i++ > 80000UL) {     /* Cable not connected */
            EMAC->CTL &= ~EMAC_CTL_OPMODE_Msk;
            EMAC->CTL &= ~EMAC_CTL_FUDUP_Msk;
            break;
        }
    }

    if(i <= 80000UL) {
        /* 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) {
            EMAC->CTL |= EMAC_CTL_OPMODE_Msk;
            EMAC->CTL |= EMAC_CTL_FUDUP_Msk;
        } else if (reg & PHY_ANLPA_DR100_TX_HALF) {
            EMAC->CTL |= EMAC_CTL_OPMODE_Msk;
            EMAC->CTL &= ~EMAC_CTL_FUDUP_Msk;
        } else if (reg & PHY_ANLPA_DR10_TX_FULL) {
            EMAC->CTL &= ~EMAC_CTL_OPMODE_Msk;
            EMAC->CTL |= EMAC_CTL_FUDUP_Msk;
        } else {
            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 = 0UL; 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 = 0UL;
        tx_desc[i].u32Next = (uint32_t)&tx_desc[(i + 1UL) % 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 = 0UL; 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 = 0UL;
        rx_desc[i].u32Next = (uint32_t)&rx_desc[(i + 1UL) % 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 = 1000000000ull * (uint64_t)subsec;
    i >>= 31;
    return((uint32_t)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 = (1ull << 31) * nsec;
    i /= 1000000000ull;
    return((uint32_t)i);
}


/*@}*/ /* end of group M480_EMAC_EXPORTED_FUNCTIONS */



/** @endcond HIDDEN_SYMBOLS */


/** @addtogroup M480_EMAC_EXPORTED_FUNCTIONS EMAC Exported 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;

    /* 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;
    while(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(0UL, 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;
    uint32_t reg;
    u32Lsw = (uint32_t)(((uint32_t)pu8MacAddr[4] << 24) |
                        ((uint32_t)pu8MacAddr[5] << 16));
    u32Msw = (uint32_t)(((uint32_t)pu8MacAddr[0] << 24)|
                        ((uint32_t)pu8MacAddr[1] << 16)|
                        ((uint32_t)pu8MacAddr[2] << 8)|
                        (uint32_t)pu8MacAddr[3]);

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

    EMAC->CAMEN |= (1UL << 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 &= ~(1UL << u32Entry);
}


/**
  * @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 = 0UL;

    /* Clear Rx interrupt flags */
    reg = EMAC->INTSTS;
    EMAC->INTSTS = reg & 0xFFFFUL;  /* 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... */
        while(1) {}
    } else {

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

        /* If we reach last recv Rx descriptor, leave the loop */
        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 & 0xFFFFUL;
                memcpy(pu8Data, (uint8_t *)desc->u32Backup1, *pu32Size);
                u32Count = 1UL;
            } 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 = 0UL;

    /* Clear Rx interrupt flags */
    reg = EMAC->INTSTS;
    EMAC->INTSTS = reg & 0xFFFFUL; /* 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... */
        while(1) {}
    } 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) {
            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 & 0xFFFFUL;
                    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 = 1UL;
                } 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;

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

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

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

    EMAC_TRIGGER_RX();
}


/**
  * @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;
    uint32_t ret = 0UL;
    /* Get Tx frame descriptor & data pointer */
    desc = (EMAC_DESCRIPTOR_T *)u32NextTxDesc;

    status = desc->u32Status1;

    /* Check descriptor ownership */
    if((status & EMAC_DESC_OWN_EMAC) != EMAC_DESC_OWN_EMAC) {
        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();
        ret = 1UL;
    }
    return(ret);
}


/**
  * @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 = 0UL;

    reg = EMAC->INTSTS;
    /* Clear Tx interrupt flags */
    EMAC->INTSTS = reg & (0xFFFF0000UL & ~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... */
        while(1) {}
    } 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 >> 16UL;
            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 = 0UL;

    reg = EMAC->INTSTS;
    /* Clear Tx interrupt flags */
    EMAC->INTSTS = reg & (0xFFFF0000UL & ~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... */
        while(1) {}
    } 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) != EMAC_DESC_OWN_EMAC) {
            /* Get Tx status stored in descriptor */
            status = desc->u32Status2 >> 16UL;
            if (status & EMAC_TXFD_TXCP) {
                u32Count = 1UL;
                *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);
}

/**
  * @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 160MHz (Real chip using PLL). Each tick is 6.25ns
       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 / (160MHz), where tick_freq = (2^31 / 215) MHz
       From above equation, addend register = 2^63 / (160M * 215) ~= 268121280 = 0xFFB34C0
       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 = 0UL;
}

/**
  * @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 M480_EMAC_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group M480_EMAC_Driver */

/*@}*/ /* end of group M480_Device_Driver */

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