/* -------------------------------------------------------------------------- 
 * Copyright (c) 2013-2016 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Date:        02. March 2016
 * $Revision:    V2.6
 *
 * Driver:       Driver_ETH_MAC0
 * Configured:   via RTE_Device.h configuration file
 * Project:      Ethernet Media Access (MAC) Driver for NXP LPC18xx
 * --------------------------------------------------------------------------
 * Use the following configuration settings in the middleware component
 * to connect to this driver.
 *
 *   Configuration Setting                     Value
 *   ---------------------                     -----
 *   Connect to hardware via Driver_ETH_MAC# = 0
 * -------------------------------------------------------------------------- */

/* History:
 *  Version 2.6
 *    - Corrected PowerControl function for conditional Power full (driver must be initialized)
 *  Version 2.5
 *    - Corrected return value of the ReadFrame function
 *  Version 2.4
 *    - Updated initialization, uninitialization and power procedures
 *  Version 2.3
 *    - Corrected return value of PHY_Read and PHY_Write functions on timeout
 *  Version 2.2
 *    - GetMacAddress function implemented in Ethernet driver
 *  Version 2.1
 *    - Added Sleep mode and Wake-up on Magic Packet 
 *    - Improved robustness and error control
 *    - Added CLK0 pin option support
 *  Version 2.0
 *    - Based on API V2.00
 *    - Added multicast MAC address filtering
 *  Version 1.1
 *    - Based on API V1.10 (namespace prefix ARM_ added)
 *  Version 1.0
 *    - Initial release
 */

/* IEEE 1588 time stamping enable (PTP) */
#ifndef EMAC_TIME_STAMP
#define EMAC_TIME_STAMP         0
#endif

#include "EMAC_LPC18xx.h"

#define ARM_ETH_MAC_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,6) /* driver version */

/* Timeouts */
#define PHY_TIMEOUT         200         /* PHY Register access timeout in us  */

/* EMAC Memory Buffer configuration */
#define NUM_RX_BUF          4           /* 0x1800 for Rx (4*1536=6K)          */
#define NUM_TX_BUF          2           /* 0x0C00 for Tx (2*1536=3K)          */
#define ETH_BUF_SIZE        1536        /* ETH Receive/Transmit buffer size   */

/* EMAC core clock (system_LPC43xx.c) */
extern uint32_t GetClockFreq (uint32_t clk_src);

/* Ethernet Pin definitions */
static const PIN_ID eth_pins[] = {
  { RTE_ENET_MDI_MDC_PORT,     RTE_ENET_MDI_MDC_PIN,                   SCU_SFS_EZI | RTE_ENET_MDI_MDC_FUNC       },
  { RTE_ENET_MDI_MDIO_PORT,    RTE_ENET_MDI_MDIO_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MDI_MDIO_FUNC      },
#if (RTE_ENET_RMII)
  { RTE_ENET_RMII_TXD0_PORT,   RTE_ENET_RMII_TXD0_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_TXD0_FUNC     },
  { RTE_ENET_RMII_TXD1_PORT,   RTE_ENET_RMII_TXD1_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_TXD1_FUNC     },
  { RTE_ENET_RMII_TX_EN_PORT,  RTE_ENET_RMII_TX_EN_PIN,  SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_TX_EN_FUNC    },
  { RTE_ENET_RMII_REF_CLK_PORT,RTE_ENET_RMII_REF_CLK_PIN,SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_REF_CLK_FUNC  },
  { RTE_ENET_RMII_RXD0_PORT,   RTE_ENET_RMII_RXD0_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_RXD0_FUNC     },
  { RTE_ENET_RMII_RXD1_PORT,   RTE_ENET_RMII_RXD1_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_RXD1_FUNC     },
  { RTE_ENET_RMII_RX_DV_PORT,  RTE_ENET_RMII_RX_DV_PIN,  SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_RMII_RX_DV_FUNC    }
#endif
#if (RTE_ENET_MII)
  { RTE_ENET_MII_TXD0_PORT,    RTE_ENET_MII_TXD0_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TXD0_FUNC      },
  { RTE_ENET_MII_TXD1_PORT,    RTE_ENET_MII_TXD1_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TXD1_FUNC      },
  { RTE_ENET_MII_TXD2_PORT,    RTE_ENET_MII_TXD2_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TXD2_FUNC      },
  { RTE_ENET_MII_TXD3_PORT,    RTE_ENET_MII_TXD3_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TXD3_FUNC      },
  { RTE_ENET_MII_TX_EN_PORT,   RTE_ENET_MII_TX_EN_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TX_EN_FUNC     },
  { RTE_ENET_MII_TX_CLK_PORT,  RTE_ENET_MII_TX_CLK_PIN,  SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TX_CLK_FUNC    },
#if (RTE_ENET_MII_TX_ER_PIN_EN)
  { RTE_ENET_MII_TX_ER_PORT,   RTE_ENET_MII_TX_ER_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_TX_ER_FUNC     },
#endif
  { RTE_ENET_MII_RXD0_PORT,    RTE_ENET_MII_RXD0_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RXD0_FUNC      },
  { RTE_ENET_MII_RXD1_PORT,    RTE_ENET_MII_RXD1_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RXD1_FUNC      },
  { RTE_ENET_MII_RXD2_PORT,    RTE_ENET_MII_RXD2_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RXD2_FUNC      },
  { RTE_ENET_MII_RXD3_PORT,    RTE_ENET_MII_RXD3_PIN,    SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RXD3_FUNC      },
  { RTE_ENET_MII_RX_DV_PORT,   RTE_ENET_MII_RX_DV_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RX_DV_FUNC     },
  { RTE_ENET_MII_RX_CLK_PORT,  RTE_ENET_MII_RX_CLK_PIN,  SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RX_CLK_FUNC    },
  { RTE_ENET_MII_RX_ER_PORT,   RTE_ENET_MII_RX_ER_PIN,   SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_RX_ER_FUNC     },
  { RTE_ENET_MII_COL_PORT,     RTE_ENET_MII_COL_PIN,     SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_COL_FUNC       },
  { RTE_ENET_MII_CRS_PORT,     RTE_ENET_MII_CRS_PIN,     SCU_SFS_EHS | SCU_SFS_EZI | RTE_ENET_MII_CRS_FUNC       },
#endif
};

/* Driver Version */
static const ARM_DRIVER_VERSION DriverVersion = {
  ARM_ETH_MAC_API_VERSION,
  ARM_ETH_MAC_DRV_VERSION
};

/* Driver Capabilities */
static const ARM_ETH_MAC_CAPABILITIES DriverCapabilities = {
  0,                                /* checksum_offload_rx_ip4  */
  0,                                /* checksum_offload_rx_ip6  */
  0,                                /* checksum_offload_rx_udp  */
  0,                                /* checksum_offload_rx_tcp  */
  0,                                /* checksum_offload_rx_icmp */
  0,                                /* checksum_offload_tx_ip4  */
  0,                                /* checksum_offload_tx_ip6  */
  0,                                /* checksum_offload_tx_udp  */
  0,                                /* checksum_offload_tx_tcp  */
  0,                                /* checksum_offload_tx_icmp */
  (RTE_ENET_RMII) ?
  ARM_ETH_INTERFACE_RMII :
  ARM_ETH_INTERFACE_MII,            /* media_interface          */
  0,                                /* mac_address              */
  1,                                /* event_rx_frame           */
  1,                                /* event_tx_frame           */
  1,                                /* event_wakeup             */
  (EMAC_TIME_STAMP) ? 1 : 0         /* precision_timer          */
};

/* Local variables */
static EMAC_CTRL  emac_control = { 0 };
#define emac     (emac_control)
static RX_Desc   rx_desc[NUM_RX_BUF];
static TX_Desc   tx_desc[NUM_TX_BUF];
static uint32_t  rx_buf [NUM_RX_BUF][ETH_BUF_SIZE>>2];
static uint32_t  tx_buf [NUM_TX_BUF][ETH_BUF_SIZE>>2];

/* Local functions */
static void init_rx_desc (void);
static void init_tx_desc (void);
static uint32_t crc32_8bit_rev (uint32_t crc32, uint8_t val);
static uint32_t crc32_data (const uint8_t *data, uint32_t len);

/**
  \fn          void init_rx_desc (void)
  \brief       Initialize Rx DMA descriptors.
  \return      none.
*/
static void init_rx_desc (void) {
  uint32_t i,next;

  for (i = 0; i < NUM_RX_BUF; i++) {
    rx_desc[i].Stat = EMAC_RDES0_OWN;
    rx_desc[i].Ctrl = EMAC_RDES1_RCH | ETH_BUF_SIZE;
    rx_desc[i].Addr = (uint8_t *)&rx_buf[i];
    next = i + 1;
    if (next == NUM_RX_BUF) next = 0;
    rx_desc[i].Next = &rx_desc[next];
  }
  ENET->DMA_REC_DES_ADDR = (uint32_t)&rx_desc[0];
  emac.rx_index = 0;
}

/**
  \fn          void init_tx_desc (void)
  \brief       Initialize Tx DMA descriptors.
  \return      none.
*/
static void init_tx_desc (void) {
  uint32_t i,next;

  for (i = 0; i < NUM_TX_BUF; i++) {
    tx_desc[i].CtrlStat = EMAC_TDES0_TCH | EMAC_TDES0_LS | EMAC_TDES0_FS;
    tx_desc[i].Addr     = (uint8_t *)&tx_buf[i];
    next = i + 1;
    if (next == NUM_TX_BUF) next = 0;
    tx_desc[i].Next     = &tx_desc[next];
  }
  ENET->DMA_TRANS_DES_ADDR = (uint32_t)&tx_desc[0];
  emac.tx_index = 0;
}

/**
  \fn          uint32_t crc32_8bit_rev (uint32_t crc32, uint8_t val)
  \brief       Calculate 32-bit CRC (Polynom: 0x04C11DB7, data bit-reversed).
  \param[in]   crc32  CRC initial value
  \param[in]   val    Input value
  \return      Calculated CRC value
*/
static uint32_t crc32_8bit_rev (uint32_t crc32, uint8_t val) {
  uint32_t n;

  crc32 ^= __RBIT (val);
  for (n = 8; n; n--) {
    if (crc32 & 0x80000000) {
      crc32 <<= 1;
      crc32  ^= 0x04C11DB7;
    } else {
      crc32 <<= 1;
    }
  }
  return (crc32);
}

/**
  \fn          uint32_t crc32_data (const uint8_t *data, uint32_t len)
  \brief       Calculate standard 32-bit Ethernet CRC.
  \param[in]   data  Pointer to buffer containing the data
  \param[in]   len   Data length in bytes
  \return      Calculated CRC value
*/
static uint32_t crc32_data (const uint8_t *data, uint32_t len) {
  uint32_t crc;

  for (crc = 0xFFFFFFFF; len; len--) {
    crc = crc32_8bit_rev (crc, *data++);
  }
  return (crc ^ 0xFFFFFFFF);
}


/* Ethernet Driver functions */

/**
  \fn          ARM_DRIVER_VERSION GetVersion (void)
  \brief       Get driver version.
  \return      \ref ARM_DRIVER_VERSION
*/
static ARM_DRIVER_VERSION GetVersion (void) {
  return DriverVersion;
}


/**
  \fn          ARM_ETH_MAC_CAPABILITIES GetCapabilities (void)
  \brief       Get driver capabilities.
  \return      \ref ARM_ETH_MAC_CAPABILITIES
*/
static ARM_ETH_MAC_CAPABILITIES GetCapabilities (void) {
  return DriverCapabilities;
}


/**
  \fn          int32_t Initialize (ARM_ETH_MAC_SignalEvent_t cb_event)
  \brief       Initialize Ethernet MAC Device.
  \param[in]   cb_event  Pointer to \ref ARM_ETH_MAC_SignalEvent
  \return      \ref execution_status
*/
static int32_t Initialize (ARM_ETH_MAC_SignalEvent_t cb_event) {
  const PIN_ID *pin;

  if (emac.flags & EMAC_FLAG_INIT) { return ARM_DRIVER_OK; }

  /* Configure EMAC pins */
  for (pin = eth_pins; pin != &eth_pins[sizeof(eth_pins)/sizeof(PIN_ID)]; pin++) {
    if (pin->port == 0x10) {
      SCU_CLK_PinConfigure (pin->num, pin->config_val);
      continue;
    }
    SCU_PinConfigure(pin->port, pin->num, pin->config_val);
  }
  
  /* Clear control structure */
  memset (&emac, 0, sizeof (EMAC_CTRL));

  emac.cb_event = cb_event;
  emac.flags    = EMAC_FLAG_INIT;

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t Uninitialize (void)
  \brief       De-initialize Ethernet MAC Device.
  \return      \ref execution_status
*/
static int32_t Uninitialize (void) {
  const PIN_ID *pin;

  emac.flags = 0;

  /* Unconfigure ethernet pins */
  for (pin = eth_pins; pin != &eth_pins[sizeof(eth_pins)/sizeof(PIN_ID)]; pin++) {
    if (pin->port == 0x10) {
      SCU_CLK_PinConfigure (pin->num, 0);
      continue;
    }
    SCU_PinConfigure(pin->port, pin->num, 0);
  }

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t PowerControl (ARM_POWER_STATE state)
  \brief       Control Ethernet MAC Device Power.
  \param[in]   state  Power state
  \return      \ref execution_status
*/
static int32_t PowerControl (ARM_POWER_STATE state) {
  uint32_t clk;

  switch (state) {
    case ARM_POWER_OFF:
      /* Disable EMAC interrupts */
      NVIC_DisableIRQ(ETHERNET_IRQn);

      /* Reset EMAC peripheral */
      LPC_RGU->RESET_CTRL0 = RGU_RESET_EMAC;
      while (!(LPC_RGU->RESET_ACTIVE_STATUS0 & RGU_RESET_EMAC));

      /* Disable EMAC peripheral clock */
      LPC_CCU1->CLK_M3_ETHERNET_CFG &= ~CCU_CLK_CFG_RUN;

      emac.flags &= ~EMAC_FLAG_POWER;
      break;

    case ARM_POWER_LOW:
      return ARM_DRIVER_ERROR_UNSUPPORTED;

    case ARM_POWER_FULL:
      if ((emac.flags & EMAC_FLAG_INIT)  == 0) { return ARM_DRIVER_ERROR; }
      if ((emac.flags & EMAC_FLAG_POWER) != 0) { return ARM_DRIVER_OK; }

      /* Enable EMAC peripheral clock */
      LPC_CCU1->CLK_M3_ETHERNET_CFG |=  CCU_CLK_CFG_AUTO | CCU_CLK_CFG_RUN;
      while (!(LPC_CCU1->CLK_M3_ETHERNET_STAT & CCU_CLK_STAT_RUN));

      /* Configure Ethernet PHY interface mode (MII/RMII) */
      /* EMAC must be reset after changing PHY interface! */
      #if (RTE_ENET_RMII)
        LPC_CREG->CREG6 = (LPC_CREG->CREG6 & ~EMAC_CREG6_ETH_MASK) | EMAC_CREG6_ETH_RMII;
      #else
        LPC_CREG->CREG6 = (LPC_CREG->CREG6 & ~EMAC_CREG6_ETH_MASK) | EMAC_CREG6_ETH_MII;
      #endif

      /* Reset EMAC peripheral */
      LPC_RGU->RESET_CTRL0 = RGU_RESET_EMAC;
      while (!(LPC_RGU->RESET_ACTIVE_STATUS0 & RGU_RESET_EMAC));

      /* Soft reset EMAC DMA controller */
      ENET->DMA_BUS_MODE |= EMAC_DBMR_SWR;
      while (ENET->DMA_BUS_MODE & EMAC_DBMR_SWR);

      /* MDC clock range selection */
      clk = GetClockFreq (CLK_SRC_PLL1);
      if      (clk >= 150000000) emac.mmar_cr_val = EMAC_MMAR_CR_Div102;
      else if (clk >= 100000000) emac.mmar_cr_val = EMAC_MMAR_CR_Div62;
      else if (clk >= 60000000)  emac.mmar_cr_val = EMAC_MMAR_CR_Div42;
      else if (clk >= 35000000)  emac.mmar_cr_val = EMAC_MMAR_CR_Div26;
      else if (clk >= 25000000)  emac.mmar_cr_val = EMAC_MMAR_CR_Div16;
      else                       return ARM_DRIVER_ERROR_UNSUPPORTED;
      ENET->MAC_MII_ADDR = emac.mmar_cr_val;

      #if (EMAC_TIME_STAMP)
        /* Enhanced DMA descriptor enable */
        ENET->DMA_BUS_MODE |= EMAC_DBMR_ATDS;

        /* Set clock accuracy to 20ns (50MHz) or 50ns (20MHz) */
        if (clk >= 51000000) {
          ENET->SUBSECOND_INCR = 20;
          ENET->ADDEND         = (50000000ull << 32) / clk;
        }
        else {
          ENET->SUBSECOND_INCR = 50;
          ENET->ADDEND         = (20000000ull << 32) / clk;
        }

        /* Enable timestamp fine update */
        ENET->MAC_TIMESTP_CTRL = EMAC_MTCR_TSIPV4E | EMAC_MTCR_TSIPV6E |
                                 EMAC_MTCR_TSCTRL  | EMAC_MTCR_TSADDR  |
                                 EMAC_MTCR_TSCFUP  | EMAC_MTCR_TSENA;
        emac.tx_ts_index = 0;
      #endif

      /* Initialize MAC configuration */
      ENET->MAC_CONFIG = EMAC_MCR_DO | EMAC_MCR_PS;

      /* Initialize Filter registers */
      ENET->MAC_FRAME_FILTER = EMAC_MFFR_DBF;
      ENET->MAC_FLOW_CTRL    = EMAC_MFCR_DZPQ;

      /* Initialize Address register */
      ENET->MAC_ADDR0_HIGH = 0x00000000;
      ENET->MAC_ADDR0_LOW  = 0x00000000;

      /* Disable MAC interrupts */
      ENET->MAC_INTR_MASK  = EMAC_MIMR_PMTIM | EMAC_MIMR_TSIM;

      /* Initialize DMA Descriptors */
      init_rx_desc ();
      init_tx_desc ();

      /* Enable DMA interrupts */
      ENET->DMA_STAT   = 0xFFFFFFFF;
      ENET->DMA_INT_EN = EMAC_DIER_NIE | EMAC_DIER_RIE | EMAC_DIER_TIE;

      /* Enable ethernet interrupts */
      NVIC_ClearPendingIRQ(ETHERNET_IRQn);
      NVIC_EnableIRQ(ETHERNET_IRQn);

      emac.frame_end = NULL;
      emac.flags    |= EMAC_FLAG_POWER;
      break;

    default:
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }
  
  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t GetMacAddress (ARM_ETH_MAC_ADDR *ptr_addr)
  \brief       Get Ethernet MAC Address.
  \param[in]   ptr_addr  Pointer to address
  \return      \ref execution_status
*/
static int32_t GetMacAddress (ARM_ETH_MAC_ADDR *ptr_addr) {
  uint32_t val;

  if (!ptr_addr) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not yet powered */
    return ARM_DRIVER_ERROR;
  }

  val = ENET->MAC_ADDR0_HIGH;
  ptr_addr->b[5] = (uint8_t)(val >> 8);
  ptr_addr->b[4] = (uint8_t)(val);
  val = ENET->MAC_ADDR0_LOW;
  ptr_addr->b[3] = (uint8_t)(val >> 24);
  ptr_addr->b[2] = (uint8_t)(val >> 16);
  ptr_addr->b[1] = (uint8_t)(val >>  8);
  ptr_addr->b[0] = (uint8_t)(val);

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t SetMacAddress (const ARM_ETH_MAC_ADDR *ptr_addr)
  \brief       Set Ethernet MAC Address.
  \param[in]   ptr_addr  Pointer to address
  \return      \ref execution_status
*/
static int32_t SetMacAddress (const ARM_ETH_MAC_ADDR *ptr_addr) {

  if (!ptr_addr) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not yet powered */
    return ARM_DRIVER_ERROR;
  }

  /* Set Ethernet MAC Address registers */
  ENET->MAC_ADDR0_HIGH = (ptr_addr->b[5] <<  8) |  ptr_addr->b[4];
  ENET->MAC_ADDR0_LOW  = (ptr_addr->b[3] << 24) | (ptr_addr->b[2] << 16) |
                         (ptr_addr->b[1] <<  8) |  ptr_addr->b[0];
  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t SetAddressFilter (const ARM_ETH_MAC_ADDR *ptr_addr,
                                               uint32_t          num_addr)
  \brief       Configure Address Filter.
  \param[in]   ptr_addr  Pointer to addresses
  \param[in]   num_addr  Number of addresses to configure
  \return      \ref execution_status
*/
static int32_t SetAddressFilter (const ARM_ETH_MAC_ADDR *ptr_addr, uint32_t num_addr) {
  uint32_t crc;

  if (!ptr_addr && num_addr) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not yet powered */
    return ARM_DRIVER_ERROR;
  }

  ENET->MAC_FRAME_FILTER &= ~(EMAC_MFFR_HPF | EMAC_MFFR_HMC);
  ENET->MAC_HASHTABLE_HIGH = 0x00000000;
  ENET->MAC_HASHTABLE_LOW  = 0x00000000;

  if (num_addr == 0) {
    return ARM_DRIVER_OK;
  }

  /* Calculate 64-bit Hash table for MAC addresses */
  for ( ; num_addr; ptr_addr++, num_addr--) {
    crc = crc32_data (&ptr_addr->b[0], 6) >> 26;
    if (crc & 0x20) {
      ENET->MAC_HASHTABLE_HIGH |= (1 << (crc & 0x1F));
    }
    else {
      ENET->MAC_HASHTABLE_LOW  |= (1 << crc);
    }
  }
  /* Enable both, unicast and hash address filtering */
  ENET->MAC_FRAME_FILTER |= EMAC_MFFR_HPF | EMAC_MFFR_HMC;

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t SendFrame (const uint8_t *frame, uint32_t len, uint32_t flags)
  \brief       Send Ethernet frame.
  \param[in]   frame  Pointer to frame buffer with data to send
  \param[in]   len    Frame buffer length in bytes
  \param[in]   flags  Frame transmit flags (see ARM_ETH_MAC_TX_FRAME_...)
  \return      \ref execution_status
*/
static int32_t SendFrame (const uint8_t *frame, uint32_t len, uint32_t flags) {
  uint8_t *dst;
  uint32_t ctrl;

  if (!frame || !len) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not yet powered */
    return ARM_DRIVER_ERROR;
  }

  dst = emac.frame_end;
  if (dst == NULL) {
    /* Start of a new transmit frame */
    if (tx_desc[emac.tx_index].CtrlStat & EMAC_TDES0_OWN) {
      /* Transmitter is busy, wait */
      return ARM_DRIVER_ERROR_BUSY;
    }
    dst = tx_desc[emac.tx_index].Addr;
    tx_desc[emac.tx_index].Size = len;
  }
  else {
    /* Sending data fragments in progress */
    tx_desc[emac.tx_index].Size += len;
  }
  /* Fast-copy data fragments to EMAC-DMA buffer */
  for ( ; len > 7; dst += 8, frame += 8, len -= 8) {
    ((__packed uint32_t *)dst)[0] = ((__packed uint32_t *)frame)[0];
    ((__packed uint32_t *)dst)[1] = ((__packed uint32_t *)frame)[1];
  }
  /* Copy remaining 7 bytes */
  for ( ; len > 1; dst += 2, frame += 2, len -= 2) {
    ((__packed uint16_t *)dst)[0] = ((__packed uint16_t *)frame)[0];
  }
  if (len > 0) dst++[0] = frame++[0];

  if (flags & ARM_ETH_MAC_TX_FRAME_FRAGMENT) {
    /* More data to come, remember current write position */
    emac.frame_end = dst;
    return ARM_DRIVER_OK;
  }

  /* Frame is now ready, send it to DMA */
  ctrl = tx_desc[emac.tx_index].CtrlStat & ~(EMAC_TDES0_IC | EMAC_TDES0_TTSE);
  if (flags & ARM_ETH_MAC_TX_FRAME_EVENT)     ctrl |= EMAC_TDES0_IC;
#if (EMAC_TIME_STAMP)
  if (flags & ARM_ETH_MAC_TX_FRAME_TIMESTAMP) ctrl |= EMAC_TDES0_TTSE;
  emac.tx_ts_index = emac.tx_index;
#endif
  tx_desc[emac.tx_index].CtrlStat = ctrl | EMAC_TDES0_OWN;

  if (++emac.tx_index == NUM_TX_BUF) emac.tx_index = 0;
  emac.frame_end = NULL;

  /* Start frame transmission */
  ENET->DMA_STAT = EMAC_DSR_TPS;
  ENET->DMA_TRANS_POLL_DEMAND = 0;

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t ReadFrame (uint8_t *frame, uint32_t len)
  \brief       Read data of received Ethernet frame.
  \param[in]   frame  Pointer to frame buffer for data to read into
  \param[in]   len    Frame buffer length in bytes
  \return      number of data bytes read or execution status
                 - value >= 0: number of data bytes read
                 - value < 0: error occurred, value is execution status as defined with \ref execution_status 
*/
static int32_t ReadFrame (uint8_t *frame, uint32_t len) {
  uint8_t const *src;
  int32_t cnt = (int32_t)len;

  if (!frame && len) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not yet powered */
    return ARM_DRIVER_ERROR;
  }

  /* Fast-copy data to packet buffer */
  src = rx_desc[emac.rx_index].Addr;
  for ( ; len > 7; frame += 8, src += 8, len -= 8) {
    ((__packed uint32_t *)frame)[0] = ((uint32_t *)src)[0];
    ((__packed uint32_t *)frame)[1] = ((uint32_t *)src)[1];
  }
  /* Copy remaining 7 bytes */
  for ( ; len > 1; frame += 2, src += 2, len -= 2) {
    ((__packed uint16_t *)frame)[0] = ((uint16_t *)src)[0];
  }
  if (len > 0) frame[0] = src[0];

  /* Return this block back to EMAC-DMA */
  rx_desc[emac.rx_index].Stat = EMAC_RDES0_OWN;

  if (++emac.rx_index == NUM_RX_BUF) emac.rx_index = 0;

  if (ENET->DMA_STAT & EMAC_DSR_RU) {
    /* Receive buffer unavailable, resume DMA */
    ENET->DMA_STAT = EMAC_DSR_RU;
    ENET->DMA_REC_POLL_DEMAND = 0;
  }
  return (cnt);
}

/**
  \fn          uint32_t GetRxFrameSize (void)
  \brief       Get size of received Ethernet frame.
  \return      number of bytes in received frame
*/
static uint32_t GetRxFrameSize (void) {
  uint32_t stat;

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not yet powered */
    return (0);
  }

  stat = rx_desc[emac.rx_index].Stat;
  if (stat & EMAC_RDES0_OWN) {
    /* Owned by DMA */
    return (0);
  }

  if ((stat & EMAC_RDES0_ES) || !(stat & EMAC_RDES0_FS) || !(stat & EMAC_RDES0_LS)) {
    /* Error, this block is invalid */
    return (0xFFFFFFFF);
  }
  return (((stat & EMAC_RDES0_FL) >> 16) - 4);
}

/**
  \fn          int32_t GetRxFrameTime (ARM_ETH_MAC_TIME *time)
  \brief       Get time of received Ethernet frame.
  \param[in]   time  Pointer to time structure for data to read into
  \return      \ref execution_status
*/
static int32_t GetRxFrameTime (ARM_ETH_MAC_TIME *time) {
#if (EMAC_TIME_STAMP)
  RX_Desc *rxd;

  if (!time) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  rxd = &rx_desc[emac.rx_index];
  if (rxd->Stat & EMAC_RDES0_OWN) {
    /* Owned by DMA */
    return ARM_DRIVER_ERROR_BUSY;
  }
  time->ns  = rxd->TimeLo;
  time->sec = rxd->TimeHi;

  return ARM_DRIVER_OK;
#else
  return ARM_DRIVER_ERROR_UNSUPPORTED;
#endif
}

/**
  \fn          int32_t GetTxFrameTime (ARM_ETH_MAC_TIME *time)
  \brief       Get time of transmitted Ethernet frame.
  \param[in]   time  Pointer to time structure for data to read into
  \return      \ref execution_status
*/
static int32_t GetTxFrameTime (ARM_ETH_MAC_TIME *time) {
#if (EMAC_TIME_STAMP)
  TX_Desc *txd;

  if (!time) {
    /* Invalid parameters */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  txd = &tx_desc[emac.tx_ts_index];
  if (txd->CtrlStat & EMAC_TDES0_OWN) {
    /* Owned by DMA */
    return ARM_DRIVER_ERROR_BUSY;
  }
  if (!(txd->CtrlStat & EMAC_TDES0_TTSS)) {
    /* No transmit time stamp available */
    return ARM_DRIVER_ERROR;
  }
  time->ns  = txd->TimeLo;
  time->sec = txd->TimeHi;
  return ARM_DRIVER_OK;
#else
  return ARM_DRIVER_ERROR_UNSUPPORTED;
#endif
}

/**
  \fn          int32_t Control (uint32_t control, uint32_t arg)
  \brief       Control Ethernet Interface.
  \param[in]   control  Operation
  \param[in]   arg      Argument of operation (optional)
  \return      \ref execution_status
*/
static int32_t Control (uint32_t control, uint32_t arg) {
  uint32_t maccr;
  uint32_t macffr;

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not powered */
    return ARM_DRIVER_ERROR;
  }

  switch (control) {
    case ARM_ETH_MAC_CONFIGURE:
      maccr = ENET->MAC_CONFIG & ~(EMAC_MCR_FES | EMAC_MCR_DM | EMAC_MCR_LM);

      /* Configure 100MBit/10MBit mode */
      switch (arg & ARM_ETH_MAC_SPEED_Msk) {
        case ARM_ETH_MAC_SPEED_10M:
#if (RTE_ENET_RMII)
          /* RMII Half Duplex Collision detection does not work */
          maccr |= EMAC_MCR_DM;
#endif
          break;
        case ARM_ETH_SPEED_100M:
          maccr |= EMAC_MCR_FES;
          break;
        default:
          return ARM_DRIVER_ERROR_UNSUPPORTED;
      }

      /* Configure Half/Full duplex mode */
      switch (arg & ARM_ETH_MAC_DUPLEX_Msk) {
        case ARM_ETH_MAC_DUPLEX_FULL:
          maccr |= EMAC_MCR_DM;
          break;
      }

      /* Configure loopback mode */
      if (arg & ARM_ETH_MAC_LOOPBACK) {
        maccr |= EMAC_MCR_LM;
      }

      if ((arg & ARM_ETH_MAC_CHECKSUM_OFFLOAD_RX) ||
          (arg & ARM_ETH_MAC_CHECKSUM_OFFLOAD_TX)) {
        /* Checksum offload is disabled in the driver */
        return ARM_DRIVER_ERROR_UNSUPPORTED;
      }

      ENET->MAC_CONFIG = maccr;

      macffr = ENET->MAC_FRAME_FILTER & ~(EMAC_MFFR_PR | EMAC_MFFR_PAM | EMAC_MFFR_DBF);
      /* Enable broadcast frame receive */
      if (!(arg & ARM_ETH_MAC_ADDRESS_BROADCAST)) {
        macffr |= EMAC_MFFR_DBF;
      }

      /* Enable all multicast frame receive */
      if (arg & ARM_ETH_MAC_ADDRESS_MULTICAST) {
        macffr |= EMAC_MFFR_PAM;
      }

      /* Enable promiscuous mode (no filtering) */
      if (arg & ARM_ETH_MAC_ADDRESS_ALL) {
        macffr |= EMAC_MFFR_PR;
      }
      ENET->MAC_FRAME_FILTER = macffr;

      break;

    case ARM_ETH_MAC_CONTROL_TX:
      /* Enable/disable MAC transmitter */
      if (arg != 0) {
        ENET->MAC_CONFIG  |= EMAC_MCR_TE;
        ENET->DMA_OP_MODE |= EMAC_DOMR_ST;
      }
      else {
        ENET->DMA_OP_MODE &= ~EMAC_DOMR_ST;
        ENET->MAC_CONFIG  &= ~EMAC_MCR_TE;
      }
      break;

    case ARM_ETH_MAC_CONTROL_RX:
      /* Enable/disable MAC receiver */
      if (arg != 0) {
        ENET->MAC_CONFIG  |= EMAC_MCR_RE;
        ENET->DMA_OP_MODE |= EMAC_DOMR_SR;
      }
      else {
        ENET->DMA_OP_MODE &= ~EMAC_DOMR_SR;
        ENET->MAC_CONFIG  &= ~EMAC_MCR_RE;
      }
      break;

    case ARM_ETH_MAC_FLUSH:
      /* Flush Tx and Rx buffers */
      if (arg & ARM_ETH_MAC_FLUSH_RX) {
        /* Stop/Start DMA Receive */
        uint32_t domr = ENET->DMA_OP_MODE;
        ENET->DMA_OP_MODE &= ~EMAC_DOMR_SR;
        init_rx_desc ();
        ENET->DMA_OP_MODE = domr;
      }
      if (arg & ARM_ETH_MAC_FLUSH_TX) {
        /* Stop/Start DMA Transmit */
        uint32_t domr = ENET->DMA_OP_MODE;
        ENET->DMA_OP_MODE &= ~EMAC_DOMR_ST;
        /* Flush transmit FIFO */
        ENET->DMA_OP_MODE |= EMAC_DOMR_FTF;
        init_tx_desc ();
        ENET->DMA_OP_MODE = domr;
      }
      break;

    case ARM_ETH_MAC_SLEEP:
      /* Enable/disable Sleep mode */
      if (arg != 0) {
        /* Enable Power Management interrupts */
        ENET->MAC_INTR_MASK    &= ~EMAC_MIMR_PMTIM;
        /* Enter Power-down, Magic packet enable */
        ENET->MAC_PMT_CTRL_STAT = EMAC_PMTR_MPE | EMAC_PMTR_PD;
      }
      else {
        /* Disable Power Management interrupts */
        ENET->MAC_INTR_MASK    |= EMAC_MIMR_PMTIM;
        ENET->MAC_PMT_CTRL_STAT = 0x00000000;
      }
      break;

    case ARM_ETH_MAC_VLAN_FILTER:
      /* Configure VLAN filter */
      ENET->MAC_VLAN_TAG = arg;
      break;

    default:
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }
  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t ControlTimer (uint32_t control, ARM_ETH_MAC_TIME *time)
  \brief       Control Precision Timer.
  \param[in]   control  Operation
  \param[in]   time     Pointer to time structure
  \return      \ref execution_status
*/
static int32_t ControlTimer (uint32_t control, ARM_ETH_MAC_TIME *time) {

#if (EMAC_TIME_STAMP)
  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not powered */
    return ARM_DRIVER_ERROR;
  }

  switch (control) {
    case ARM_ETH_MAC_TIMER_GET_TIME:
      /* Get current time */
      time->sec = ENET->SECONDS;
      time->ns  = ENET->NANOSECONDS;
      break;

    case ARM_ETH_MAC_TIMER_SET_TIME:
      /* Set new time */
      ENET->SECONDSUPDATE     = time->sec;
      ENET->NANOSECONDSUPDATE = time->ns;
      /* Initialize precision timer */
      ENET->MAC_TIMESTP_CTRL |= EMAC_MTCR_TSINIT;
      break;

    case ARM_ETH_MAC_TIMER_INC_TIME:
      /* Increment current time */
      ENET->SECONDSUPDATE     = time->sec;
      ENET->NANOSECONDSUPDATE = time->ns;
      /* Update precision timer */
      ENET->MAC_TIMESTP_CTRL |=  EMAC_MTCR_TSUPDT;
      break;

    case ARM_ETH_MAC_TIMER_DEC_TIME:
      /* Decrement current time */
      ENET->SECONDSUPDATE     = time->sec;
      ENET->NANOSECONDSUPDATE = time->ns | 0x80000000;
      /* Update precision timer */
      ENET->MAC_TIMESTP_CTRL |=  EMAC_MTCR_TSUPDT;
      break;

    case ARM_ETH_MAC_TIMER_SET_ALARM:
      /* Set alarm time */
      ENET->TARGETSECONDS     = time->sec;
      ENET->TARGETNANOSECONDS = time->ns;
      /* Enable timestamp interrupt trigger */
      ENET->MAC_TIMESTP_CTRL |= EMAC_MTCR_TSTRIG;
      if (time->sec || time->ns) {
        /* Enable timestamp interrupts */
        ENET->MAC_INTR_MASK &= ~EMAC_MIMR_TSIM;
        break;
      }
      /* Disable timestamp interrupts */
      ENET->MAC_INTR_MASK |= EMAC_MIMR_TSIM;
      break;

    case ARM_ETH_MAC_TIMER_ADJUST_CLOCK:
      /* Adjust current time, fine correction */
      /* Correction factor is Q31 (0x80000000 = 1.000000000) */
      ENET->ADDEND = ((uint64_t)time->ns * ENET->ADDEND) >> 31;
      /* Update addend register */
      ENET->MAC_TIMESTP_CTRL |= EMAC_MTCR_TSADDR;
      break;

    default:
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }
  return ARM_DRIVER_OK;
#else
  return ARM_DRIVER_ERROR_UNSUPPORTED;
#endif
}

/**
  \fn          int32_t PHY_Read (uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
  \brief       Read Ethernet PHY Register through Management Interface.
  \param[in]   phy_addr  5-bit device address
  \param[in]   reg_addr  5-bit register address
  \param[out]  data      Pointer where the result is written to
  \return      \ref execution_status
*/
static int32_t PHY_Read (uint8_t phy_addr, uint8_t reg_addr, uint16_t *data) {
  uint32_t tick;

  if (!data) {
    /* Invalid parameter */
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not powered */
    return ARM_DRIVER_ERROR;
  }

  ENET->MAC_MII_ADDR  = emac.mmar_cr_val | EMAC_MMAR_GB |
                        (phy_addr << 11) | (reg_addr << 6);

  /* Wait until operation completed */
  tick = osKernelSysTick();
  do {
    if (!(ENET->MAC_MII_ADDR & EMAC_MMAR_GB)) {
      *data = ENET->MAC_MII_DATA & EMAC_MMDR_GD;
      return ARM_DRIVER_OK;
    }
  } while ((osKernelSysTick() - tick) < osKernelSysTickMicroSec(PHY_TIMEOUT));

  if (!(ENET->MAC_MII_ADDR & EMAC_MMAR_GB)) {
    *data = ENET->MAC_MII_DATA & EMAC_MMDR_GD;
    return ARM_DRIVER_OK;
  }
  return ARM_DRIVER_ERROR_TIMEOUT;
}

/**
  \fn          int32_t PHY_Write (uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
  \brief       Write Ethernet PHY Register through Management Interface.
  \param[in]   phy_addr  5-bit device address
  \param[in]   reg_addr  5-bit register address
  \param[in]   data      16-bit data to write
  \return      \ref execution_status
*/
static int32_t PHY_Write (uint8_t phy_addr, uint8_t reg_addr, uint16_t data) {
  uint32_t tick;

  if (!(emac.flags & EMAC_FLAG_POWER)) {
    /* Driver not powered */
    return ARM_DRIVER_ERROR;
  }

  ENET->MAC_MII_DATA  = data;
  ENET->MAC_MII_ADDR  = emac.mmar_cr_val | EMAC_MMAR_GB | EMAC_MMAR_W |
                        (phy_addr << 11) | (reg_addr << 6);

  /* Wait until operation completed */
  tick = osKernelSysTick();
  do {
    if (!(ENET->MAC_MII_ADDR & EMAC_MMAR_GB)) {
      return ARM_DRIVER_OK;
    }
  } while ((osKernelSysTick() - tick) < osKernelSysTickMicroSec(PHY_TIMEOUT));

  if (!(ENET->MAC_MII_ADDR & EMAC_MMAR_GB)) {
    return ARM_DRIVER_OK;
  }
  return ARM_DRIVER_ERROR_TIMEOUT;
}

/**
  \fn          void ETH_IRQHandler (void)
  \brief       Ethernet Interrupt handler.
*/
void ETH_IRQHandler (void) {
  uint32_t stat,event = 0;

  stat = ENET->DMA_STAT;
  ENET->DMA_STAT = stat & (EMAC_DSR_NIS | EMAC_DSR_RI | EMAC_DSR_TI);
  if (stat & EMAC_DSR_TI) {
    /* Transmit interrupt */
    event |= ARM_ETH_MAC_EVENT_TX_FRAME;
  }
  if (stat & EMAC_DSR_RI) {
    /* Receive interrupt */
    event |= ARM_ETH_MAC_EVENT_RX_FRAME;
  }
  stat = ENET->MAC_INTR;
#if (EMAC_TIME_STAMP)
  if (stat & EMAC_MISR_TS) {
    /* Timestamp interrupt */
    if (ENET->TIMESTAMPSTAT & EMAC_MTSR_TSTARGT) {
      /* Alarm trigger interrupt */
      event |= ARM_ETH_MAC_EVENT_TIMER_ALARM;
    }
  }
#endif
  if (stat & EMAC_MISR_PMT) {
    /* Power management interrupt */
    if (ENET->MAC_PMT_CTRL_STAT & EMAC_PMTR_MPR) {
      /* Magic packet received */
      event |= ARM_ETH_MAC_EVENT_WAKEUP;
    }
  }
  /* Callback event notification */
  if (event && emac.cb_event) {
    emac.cb_event (event);
  }
}

/* MAC Driver Control Block */
ARM_DRIVER_ETH_MAC Driver_ETH_MAC0 = {
  GetVersion,
  GetCapabilities,
  Initialize,
  Uninitialize,
  PowerControl,
  GetMacAddress,
  SetMacAddress,
  SetAddressFilter,
  SendFrame,
  ReadFrame,
  GetRxFrameSize,
  GetRxFrameTime,
  GetTxFrameTime,
  ControlTimer,
  Control,
  PHY_Read,
  PHY_Write
};
